開發與維運

初入項目,JS可能遇到的問題優化以及處理方法

ES6篇

1.Array.from()

為什麼先提到Array.from(),在開發中發現數組才是最常見的一種格式,不僅是在渲染列表,表格,還是在數據請求上,都有著重要的意義。

首先,先看看Array.from()定義,它可以將一個類數組或可遍歷對象轉成一個真實的數組。所謂類數組,就是我們在非箭頭函數中所屬的arguments類型等等;可遍歷對象,便如Map類型等等。

使用規則為

Array.from(arrayLike, mapFn, thisArg)
/*
arrayLike: 必選,1、類數組(argumentg)2、可迭代對象(set,map)。
mapFn: 可選,相當於Array.from(arrayLike).map(mapFn, thisArg)。
thisArg: 可選,執行回調函數mapFn時候的this對象。非常有用,利於解耦。可以把被處理的數據和對象分離,thisArg中定義處理函數handle,用來在mapFn中返回調用handle之後的結果。
*/

功能上他能實現什麼呢?

1.將string類型轉為數組

 

const str = 'wangjingyu';
const arr = Array.from(str);
console.log(arr);
// (10) ["w", "a", "n", "g", "j", "i", "n", "g", "y", "u"]

2.將Set類型轉為數組

const set = new Set([1,2,3]);
const arr = Array.from(set);
console.log(arr);
// (3) [1, 2, 3]

3.將Map類型轉為數組

const map = new Map([[1,2,3],[4,5,6]]);
const arr = Array.from(map);
console.log(arr);
// [ [ 1, 2, 3 ], [ 4, 5, 6 ] ]

4.將類數組arguments轉為數組

const fun = function(a,b,c){
    const args = Array.from(arguments);
    console.log(args)
}
fun(1,2,3)
// [ 1, 2, 3 ]

5.使用第二個參數構造函數返回新數組

const newArr = Array.from([1,2,3], value => value * value);
console.log(newArr);
// [ 1, 4, 9 ]
const lenArr = Array.from({length: 10}, (v, i) => i + 10);
console.log(lenArr);  
// [10, 11, 12, 13, 14,15, 16, 17, 18, 19]

6.數組去重合並

const arr_a = [1,2,3];
const arr_b = [2,3,4];
const arr_c = [3,4,5];
const combine = function(a,b,c){
    const arrAll = [].concat.apply([],arguments);
    console.log(Array.from(new Set(arrAll)))
}
combine(arr_a,arr_b,arr_c);
// [ 1, 2, 3, 4, 5 ]

7.獲取數組對象中的某指定屬性的所有值

const cities = [
    { name: 'Paris', visited: 'no' },
    { name: 'Lyon', visited: 'no' },  
    { name: 'Marseille', visited: 'yes' },  
    { name: 'Rome', visited: 'yes' },  
    { name: 'Milan', visited: 'no' },  
    { name: 'Palermo', visited: 'yes' },  
    { name: 'Genoa', visited: 'yes' },  
    { name: 'Berlin', visited: 'no' },  
    { name: 'Hamburg', visited: 'yes' },  
    { name: 'New York', visited: 'yes' }
 ];
console.log(Array.from(cities, ({name}) => name))
// (10) ["Paris", "Lyon", "Marseille", "Rome", "Milan", "Palermo", "Genoa", "Berlin", "Hamburg", "New York"]

8.採用第三個參數進行數據和對象抽離

const arr = [1,2,3,4,5];
const obj = {
    double: x => x * 2,
    triple: x => x * 3,
    sqrt: x => x * x,
}
console.log(Array.from(arr, function(x){ return this.triple(x) }, obj))
// [ 3, 6, 9, 12, 15 ]

通過Array.from()方法,我們可以使用更少的代碼完成我們需要做到的功能。

2.解構原始數據

我們大多數在使用對象時候,會將一個大對象分出小的屬性,屬性值來使用,其中,我們可能會覺得某一個對象裡面的屬性值太多,而我們只需要其中的若干個,這裡我們就可以使用這種方法來做解構。

const rawUser = {  
    name: 'WangJingyu',
    surname: 'Jack',  
    email: '[email protected]',  
    displayName: 'WhmJack',  
    joined: '2021-03-04',  
    image: 'github',  
    followers: 1000
}
let user = {}, userDetails = {};
({name:user.name,surname:user.surname,...userDetails} = rawUser)
console.log(user,userDetails)
// { name: 'WangJingyu', surname: 'Jack' }  
// {
//   email: '[email protected]',
//   displayName: 'WhmJack',
//   joined: '2021-03-04',
//   image: 'github',
//   followers: 1000
// }

這樣我們就可以把自己需要的某幾個對象屬性取出來來使用了。

3.動態屬性名

在ES5時代,我們獲取屬性一般有兩種方式,要不是直接.xxx,要不就是定義變量名去通過[]獲取:

const person = {
    name : 'wjy',
    age : 23,
}
const attName = 'name';
console.log(person[attName],person.name)  
// wjy wjy

我們可以把attName作為變量名,但是只能在對象之外對他做賦值操作,並不能做到讓person對象的屬性動態變化。可能有的人要說,那我把那麼當成一個變量就好了,就比如:

const attName = 'name';
const person = {
    attName : 'wjy',
    age : 23,
}
console.log(person[attName],person.attName)  
// undefined wjy

好像不太行的亞子,我們來看看原因,person.attName獲取的屬性就是字符串attName,所以可以獲取到,而person[attName]獲取的則是person.name,但是我們的attName在person對象裡只是一個字符串,並不是一個變量,所以獲取不到,那麼怎麼辦呢?

ES6中提出了把屬性名用[]包起來的方法,在括號中就可以使用前面定義的變量了。就是:

const attName = 'sex';
const person = {
    name : 'wjy',
    [attName] : 'male',
    age : 23,
}
console.log(person[attName])
// male

這樣我們就可以動態設定對象的屬性名了。  

4.symbol的對象使用

作為ES6新提出的變量類型,雖然symbol屬性已經出來很久了,但是很多時候我們還是很少使用它,這裡提到一種小小的用法,僅僅作為拋磚,更多的方法等著各位dalao來補充擴展。

對於一個已經存在的對象,裡面可能存在一個屬性key,名字暫且為'id',在多人開發時候可能會有其他人也對此對象做了內部屬性命名為id的操作,或者說他繼承了這個對象,並且對這個對象做了重寫,那麼這樣會導致這個對象的id被覆蓋。從而出現一些問題。這個時候就可以讓symbol大顯身手了。

const custId = Symbol.for('id');
const obj = {
    [custId] : '0100110100',
    'id' : '23322232',
    'iq' : '150',
};
console.log(obj);
// { id: '23322232', iq: '150', [Symbol(id)]: '0100110100' }

那麼問題來了,我們怎麼把他取出來呢,如果採用常規的for循環,結果是這樣的:

for(let [key,value] of Object.entries(obj)){
    console.log('let of',key,value)
}
// let of id 23322232
// let of iq 150

確實不太行,我們可以用symbol的api去獲取:

Object.getOwnPropertySymbols(obj).forEach((item) => {
    console.log(obj[item])
})
// 0100110100

確實是可以的,但是如果我們想for循環整體obj呢,這時候可以使用反射Reflect的相關api:

Reflect.ownKeys(obj).forEach(item => {
    console.log(obj[item])
})
// 23322232
// 150
// 0100110100

這樣我們就可以把值取出來了,並且達到了我們的效果。

分支優化篇

這一塊主要是針對if else做的一個優化方法策略的整理總結,在做項目中,難免會出現越來越多的判斷情況,而我們也需要根據這些判斷數值來做選擇,兩個三個分支選擇還好,如果選擇多了起來,那麼我們可能就會出現這樣的情況:

const ifOperation = (status) => {
  if(status === 1){
    consoleLog('one');
    consoleRun('oneEvent');
  } else if(status === 2){
    consoleLog('two');
    consoleRun('TwoEvent');
  } else if(status === 222){
    consoleLog('two');
    consoleRun('TwoEvent');
  } else if(status === 3){
    consoleLog('three');
    consoleRun('ThreeEvent');
  } else if(status === 4){
    consoleLog('four');
    consoleRun('FourEvent');
  } else {
    consoleLog('other');
    consoleRun('OtherEvent');
  }
}

雖然只有六個分支,但是已經看起來臃腫不堪了,可能我們可以把某一個else if變得厚重一下?

const ifOperation = (status) => {
  if(status === 1){
    consoleLog('one');
    consoleRun('oneEvent');
  } else if(status === 2 || status === 222){
    consoleLog('two');
    consoleRun('TwoEvent');
  } else if(status === 3){
    consoleLog('three');
    consoleRun('ThreeEvent');
  } else if(status === 4){
    consoleLog('four');
    consoleRun('FourEvent');
  } else {
    consoleLog('other');
    consoleRun('OtherEvent');
  }
}

看起來可能好一些,不過更多的人應該會選擇switch case,那麼它就會變成:

const switchOperation = (status) => {
  switch(status){
    case 1:
      consoleLog('one');
      consoleRun('oneEvent');
      break;
    case 2:
    case 222:
      consoleLog('two');
      consoleRun('TwoEvent');
      break;
    case 3:
      conosleLog('three');
      consoleRun('ThreeEvent');
      break;
    case 4:
      consoleLog('four');
      consoleRun('FourEvent');
      break;
    default:
      consoleLog('other');
      consoleRun('OtherEvent');
      break;
  }
}

在工作中其實這種已經是我們的常態了,不過我們可以更進一步,藉助其他數據類型幫助我們簡化代碼,比如用個對象存儲if else的各種情況:

const obj = {
  1 : ['one','oneEvent'],
  2 : ['two','TwoEvent'],
  3 : ['three','ThreeEvent'],
  4 : ['four','FourEvent'],
  222 : ['two','TwoEvent'],
  'default' : ['other', 'OtherEvent']
}
const consoleLog = (status) => {
  console.log(status);
}
const consoleRun = (status) => {
  console.log(status);
}
const objOperation = (status) => {
  let operation = obj[status] || obj['default'];
  consoleLog(operation[0]);
  consoleRun(operation[1])
}
objOperation('222')

這樣就清爽很多了,比如我在做播放錄音點擊修改倍速時候可以寫的更加精簡,比如:

// 設置點擊修改倍速條件
const obj = {
    1: [1.5],
    1.5: [2],
    2: [0.5],
    0.5: [1],
};
const objOperation = (status) => {
    const operation = obj[status];
    const speedChoose = operation[0];
    setSpeed(speedChoose);
};
objOperation(speed);

當然,如果你不想用Object表示,你還可以用Map來表示呀:

const map = new Map([
  [1 , ['one','oneEvent']],
  [2 , ['two','TwoEvent']],
  [3 , ['three','ThreeEvent']],
  [4 , ['four','FourEvent']],
  [222 , ['two','TwoEvent']],
  ['default' , ['other', 'OtherEvent']]
])
const consoleLog = (status) => {
  console.log(status);
}
const consoleRun = (status) => {
  console.log(status);
}
const mapOperation = (status) => {
  let operation = map.get(status) || map.get('default');
  consoleLog(operation[0]);
  consoleRun(operation[1])
}
mapOperation(222)

不過,在Object對象和Map對象都能使用的情況下,我們優先選擇哪種呢?

1.Object一般是有自己的原型的,所以每個對象都是有一個prototype鍵

2.Object對象的鍵一般可以是數字類型,字符串類型以及symbol類型,但是一個Map的鍵可以是任意類型,使用起來更為方便

3.對於獲取鍵值對個數之類,Map可以直接用map.size獲取,而Object則需要先用Object.key()獲取所有的鍵數組,然後再去獲取length

現在我們可能需要做更多的事情,比如我們現在有兩層判斷了,不僅僅需要判斷條件a,還要判斷條件b,比如:

/*
 * param {number} status 表示狀態
 * param {string} roommate 表示舍友名稱
*/
const ifOperation = (status,roommate) => {
    if(roommate === 'ly'){
        if(status === 1){
            consoleLog('lyone');
        } else if(status === 2){
            consoleLog('lytwo');
        } else if(status === 3){
            consoleLog('lythree');
        } else if(status === 4){
            consoleLog('lyfour');
        } else {
            consoleLog('sbother');
        }
    } else if(roommate === 'wjy'){
        if(status === 1){
            consoleLog('wjyone');
        } else if(status === 2){
            consoleLog('wjytwo');
        } else if(status === 3){
            consoleLog('wjythree');
        } else if(status === 4){
            consoleLog('wjyfour');
        } else {
            consoleLog('sbother');
        }
    } else {
        consoleLog('sbother');
    }
}

這。。。看起來也太長了,如果說單層判斷還可以接受,那麼這種長度的判斷,看起來確實有點難受,不過這種情況也很普遍,可能我遇到了這樣的問題,也會首先採用這種模式去進行代碼的編寫。但是我們要知道,一旦判斷層級多了一級,那麼我們增加的條件就是2(n)倍,這時候要怎麼去寫呢?我們仍然可以採用map或者object去處理:

const map = new Map([
    ['ly_1', () => {consoleLog('lyone');consoleRun('ly_1')}],
    ['ly_2', () => {consoleLog('lytwo');consoleRun('ly_2')}],
    ['ly_3', () => {consoleLog('lythree');consoleRun('ly_3')}],
    ['ly_4', () => {consoleLog('lyfour');consoleRun('ly_4')}],
    ['wjy_1', () => {consoleLog('wjyone');consoleRun('wjy_1')}],
    ['wjy_2', () => {consoleLog('wjytwo');consoleRun('wjy_2')}],
    ['wjy_3', () => {consoleLog('wjythree');consoleRun('wjy_3')}],
    ['wjy_4', () => {consoleLog('wjyfour');consoleRun('wjy_4')}],    
    ['default', () => {consoleLog('sbother');consoleRun('other roommate')}],
])
const mapOperation = (status,roommate)=>{
  let mapAction = map.get(`${roommate}_${status}`) || map.get('default')
  mapAction.call(this)
}
mapOperation(1,'wjy')
// wjyone
// wjy_1

用Object來寫也是一樣,如下:

const obj = {
    'ly_1': () => {consoleLog('lyone');consoleRun('ly_1')},
    'ly_2': () => {consoleLog('lytwo');consoleRun('ly_2')},
    'ly_3': () => {consoleLog('lythree');consoleRun('ly_3')},
    'ly_4': () => {consoleLog('lyfour');consoleRun('ly_4')},
    'wjy_1': () => {consoleLog('wjyone');consoleRun('wjy_1')},
    'wjy_2': () => {consoleLog('wjytwo');consoleRun('wjy_2')},
    'wjy_3': () => {consoleLog('wjythree');consoleRun('wjy_3')},
    'wjy_4': () => {consoleLog('wjyfour');consoleRun('wjy_4')},  
    'default': () => {consoleLog('sbother');consoleRun('other roommate')},
}
const objOperation = (status,roommate)=>{
  let objAction = obj[`${roommate}_${status}`] || obj['default']
  objAction.call(this)
}
objOperation(1,'wjy')
// wjyone
// wjy_1

這類方法的核心就是把兩個條件拼接成一個unique的字符串,然後將拼接的字符串作為主鍵,來進行對應的函數處理,從原理上講,條件層級越多,這個方法就越簡單越省事。當然,可能有些同學覺得這樣不夠規範,覺得下劃線不正規,也可以採用對象方式。

const map = new Map([
    [{status:'1',roommate:'ly'}, () => {consoleLog('lyone');consoleRun('ly_1')}],
    [{status:'2',roommate:'ly'}, () => {consoleLog('lytwo');consoleRun('ly_2')}],
    [{status:'3',roommate:'ly'}, () => {consoleLog('lythree');consoleRun('ly_3')}],
    [{status:'4',roommate:'ly'}, () => {consoleLog('lyfour');consoleRun('ly_4')}],
    [{status:'1',roommate:'wjy'}, () => {consoleLog('wjyone');consoleRun('wjy_1')}],
    [{status:'2',roommate:'wjy'}, () => {consoleLog('wjytwo');consoleRun('wjy_2')}],
    [{status:'3',roommate:'wjy'}, () => {consoleLog('wjythree');consoleRun('wjy_3')}],
    [{status:'4',roommate:'wjy'}, () => {consoleLog('wjyfour');consoleRun('wjy_4')}],    
    ['default', () => {consoleLog('sbother');consoleRun('other roommate')}],
])
const mapOperation = (status,roommate)=>{
  let mapAction = [...map].filter(([key,value])=>(key.roommate === roommate && key.status === status))
  if(mapAction.length === 0){
    mapAction = map.get('default');
    mapAction.call(this);
  } else {
    mapAction.forEach(([key,value])=>value.call(this))      
  }
}

這樣我們就可以看到Object和Map的一個主要區別了,在使用Map類型時候我們可以選擇通過對象來做為key值。當然還有可能出現這種情況,比如同一個方法在某幾個條件中都被同時使用,並且傳了相同的參數比如:

const map = new Map([
    [{status:'1',roommate:'ly'}, () => {consoleRun('ly')}],
    [{status:'2',roommate:'ly'}, () => {consoleRun('ly')}],
    [{status:'3',roommate:'ly'}, () => {consoleRun('ly')}],
    [{status:'4',roommate:'ly'}, () => {consoleRun('ly4')}],
    ['default', () => {consoleRun('other roommate')}],
])

這個時候就可以採用正則去進行了,把前四條作為一個整體去匹配正則,比如:

const map = () => {
    return new Map([
        [/^ly_[1,4]$/, () => consoleRun('ly')],
        [/^default$/, () => {consoleLog('sbother');consoleRun('other roommate')}],
    ])  
}

通過正則去做匹配,當然這也是由於Map類型可以將各種類型作為key。下面舉一個更為具體的小例子,由於本人能力有限可能有很多沒想到的地方,所以寫出的代碼可能還是有些冗餘,歡迎大家提意見,先看看古老的if else方式:

const ifOperation = (status,roommate) => {
    if(roommate === 'ly'){
        if(status === 1){
            consoleLog('lyone');
        } else if(status === 2){
            consoleLog('lytwo');
        } else if(status === 3){
            consoleLog('lythree');
        } else if(status === 4){
            consoleLog('lyfour');
        } else {
            consoleLog('lyother');
        }
    } else if(roommate === 'wjy'){
        if(status === 1){
            consoleLog('wjyone');
        } else if(status === 2){
            consoleLog('wjytwo');
        } else if(status === 3){
            consoleLog('wjythree');
        } else if(status === 4){
            consoleLog('wjyfour');
        } else {
            consoleLog('wjyother');
        }
    } else {
        consoleLog('sbother');
    }
}

這裡可以發現,和前面只有一點不同,就是三個else分別是三個不同的條件,這樣用default處理就要分成三部分,這裡採用正則試試看咯:

const map = () => {
    return new Map([
        [/^ly_1$/, () => consoleLog('lyone')],
        [/^ly_2$/, () => consoleLog('lytwo')],
        [/^ly_3$/, () => consoleLog('lythree')],
        [/^ly_4$/, () => consoleLog('lyfour')],
        [/^ly_.*$/, () => consoleLog('lyother')],
        [/^wjy_1$/, () => consoleLog('wjyone')],
        [/^wjy_2$/, () => consoleLog('wjytwo')],
        [/^wjy_3$/, () => consoleLog('wjythree')],
        [/^wjy_4$/, () => consoleLog('wjyfour')],    
        // [/^ly_((?![1-4]{1}[\S]{0}).)*|^ly_[1-4]{2,}$/g, () => consoleLog('lyother')],
        [/^wjy_.*$/, () => consoleLog('wjyother')],
        [/^.*.*$/, () => consoleLog('sbother')],
    ])
}
const mapArray = new Map([
    [2, (operation) => operation.pop()],
    [3, (operation) => {
        // console.log(operation);
        operation.pop();    
        operation.pop();
    }],
    ['default', () => {}],
])
const mapOperation = (status,roommate) => {
    let operation = [...map()].filter(([key,value]) => {
        if(key.test(`${roommate}_${status}`)){
            return value;            
        }
    });
    let operationArr = mapArray.get(operation.length) || mapArray.get('default');
    operationArr.call(this,operation);
    operation.forEach(([key,value]) => value.call(this))
}
mapOperation(10000,'ly1')

原理很簡單,這裡我使用了兩層判斷邏輯,首先是進行正則表達式的編寫,由於有三個else,所以可能我們需要三個default,從正則角度來說就是任意字符了,這裡匹配會出現三種可能性,如果沒有匹配到,那麼只會存在一種可能為```[ [ /^.*.*$/, [Function (anonymous)] ] ]```,如果roommate正確匹配,而status沒有正確匹配,那麼會出現兩種情況,我將一定匹配的可能在數組存在最後就可以將其彈出,就可以獲得真實匹配正則,另外如果完全正確,那麼三個正則都會成功匹配,我們就需要彈出最後兩個,只取第一個。而彈出正則又是一個if else,我就放到了另一個map中,從而實現需求。

React hook篇

來到公司一開始做了一個前後端聯調的小項目,首先使用的就是React hook,那個時候還是不夠熟悉,後來參加其他項目又用了16.8之前的生命週期函數,最近重歸hook,發現項目中很多人都把組件的狀態存在了model裡,不過我還是覺得父子組件傳值傳函數這類基礎要拿出來整理一下,恰巧最近事情不多,就整理一下。

整體上分成四類:

1.父組件傳值給子組件

2.父組件傳方法給子組件

3.子組件傳值給父組件

4.子組件傳方法給父組件

1.父組件傳值給子組件

最簡單的最常用的就是父組件傳值給子組件,一般採用props傳值,我寫了一個最基本的例子:

父組件:

import React,{useState} from 'react';
import {Button} from 'antd';
import styles from './HomePage.less';
import Children from './components/Children';
const HomePage = () => {
  const [value,setValue] = useState('first');
  return (
    <div className={styles.root}>
      <Button onClick={() => {
        setValue('second')
      }}>change</Button>
      <Children value={value} />
    </div>
  )
}
export default HomePage;

子組件:

import React from "react";
import PropTypes from "prop-types";
import styles from "./Children.less";
const Children = (props) => {
  const { value } = props;
  return (
    <div className={`${styles.root}`} >
      {value}
    </div>
  );
};
Children.propTypes = {
  value:PropTypes.string
};
Children.defaultProps = {
  value:''
};
export default Children;

這是最簡單的,也是最常用的,基本任何一個項目都會用到,也是我們一般抽出業務組件最基本的類型。

2.父組件傳方法給子組件/子組件傳值給父組件

個人理解這兩類其實是一類,只是一個大的集合和一個小的集合的區別,就是說父組件傳方法給子組件是一個大的模塊,而恰巧這個模塊中囊括了子組件傳值給父組件這一個功能。舉個例子:

父組件:

import React,{useState} from 'react';
import styles from './HomePage.less';
import Children from './components/Children';
const HomePage = () => {
  const [value, setvalue] = useState('')
  const method = (val) => {
    setvalue(val);
  }
  return (
    <div className={styles.root}>
      <Children method={method} />
      {value}
    </div>
  )
}
export default HomePage;

子組件:

import React from "react";
import PropTypes from "prop-types";
import {Button} from 'antd';
import styles from "./Children.less";
const Children = (props) => {
  const { method } = props;
  return (
    <div className={`${styles.root}`} >
      <Button onClick={() => {
        method('children')
      }}>children</Button>
    </div>
  );
};
Children.propTypes = {
  method:PropTypes.func
};
Children.defaultProps = {
  method:() => {}
};
export default Children;

這裡的例子我寫的最基本,從子組件傳值給父組件的角度上講,就是子組件傳了一個value給父組件,讓父組件帶它去做他該做的事。那麼,從父組件傳方法給子組件上講,就是父組件傳了一個叫做method的方法給了子組件,這個方法裡,父組件想幹什麼就幹什麼,只需要子組件去調用就好了。當然,他們最天衣無縫的配合就是父組件已經把方法寫好了,只需要子組件傳值來讓父組件方法正常運轉就好了,perfect!

3.子組件傳方法給父組件

其實掌握了前面兩種基礎方法,在大多數項目已經夠用了,簡單的組件傳值用props,以及基於他的派生策略,複雜的可以在model裡存值去做傳遞,不過偶爾我們也需要在父組件中調用子組件的方法。我之前就遇到了這麼個問題,寫了一個大組件內容過多,我需要把他提出來,然後發現我使用了太多的useState,而且把子組件提出來後,發現事件是父組件的事件,但是需要修改子組件的state狀態,於是我就把修改子組件state的地方封裝成一個函數,供父組件去調用就好了。這裡舉個最簡單的例子:

父組件:

import React,{useRef} from 'react';
import styles from './HomePage.less';
import {Button} from 'antd';
import Children from './components/Children';
const HomePage = () => {
  const childRef = useRef<any>();
  return (
    <div className={styles.root}>
      <Children cRef={childRef} />
      <Button onClick={() => {
        childRef.current.childrenMethod();
      }}>click</Button>
    </div>
  )
}
export default HomePage;

子組件

import React,{useImperativeHandle,useState} from "react";
import PropTypes from "prop-types";
import {Button} from 'antd';
import styles from "./Children.less";
const Children = (props) => {
  const { cRef } = props;
  const [value, setvalue] = useState('')
  useImperativeHandle(cRef,() => ({
      childrenMethod:() => {
        setvalue('children');
      }
    }),
  );
  return (
    <div className={`${styles.root}`} >
      {value}
    </div>
  );
};
Children.propTypes = {
  cRef:PropTypes.object
};
Children.defaultProps = {
  cRef:{}
};
export default Children;

說起來useRef也是很神奇的,在這裡,我們的子組件暴露給父組件的方法是childrenMethod,並且做了一個指代,父組件通過這個指代獲取到子組件方法,從而進行調用。當然這裡我只是針對hook做的分析,如果是類組件裡,就要使用createRef了。需要了解的也可以看看這個文章,個人感覺還不錯:https://zhuanlan.zhihu.com/p/269580627

到這裡,對於父子組件傳值,傳函數都做了一個介紹和描述,作為新上手項目的同學來說,可以從我這個基礎例子出發,去感受React編程更為神祕的地方。

結語

之前在分享React生命週期之後,就想對JS一些我用到的以及一些想要在接下來項目中用到的小知識點做一些整理,當然我接觸項目時間也不久,暫時就統計到了這些,後續有新的收穫,還會持續統計更新。

Leave a Reply

Your email address will not be published. Required fields are marked *