深度解鎖React Hook 常用Api

前言:HOOK作為React16.8的新增特性,相信絕大多數(shù)react開發(fā)者都已經(jīng)了解或者上手過。本文希望通過對這些常用API的使用案例說明,和大家一起去深度解鎖HOOK。

1、useState

const [state, setState] = useState(initState);

1.1、以上語句大家需要理解下,useState的返回值實(shí)際上是一個長度為2的數(shù)組,所以下面的寫法也是可以的,只是為了代碼的簡潔性而使用了數(shù)組的解構(gòu)賦值。

const fruitStateVariable = useState('banana'); // 返回一個有兩個元素的數(shù)組
const fruit = fruitStateVariable[0]; // 數(shù)組里的第一個值
const setFruit = fruitStateVariable[1]; // 數(shù)組里的第二個值

注:所以參考useState的返回值,有時候我們在寫公用方法的返回值時,Object在某些時候不一定是首選項,實(shí)際上數(shù)組更方便使用者去自定義接收的變量名。

1.2、在定時器或者接口的回調(diào)函數(shù)中,有時候受制于閉包環(huán)境的影響,我們無法去獲取到state的最新狀態(tài)。實(shí)際上在setState這個更新函數(shù)中,允許接收一個函數(shù),這個函數(shù)的入?yún)⒕褪亲钚碌膕tate。

const [count, setCount] = useState(0);
useEffect(() => {
  setInterval(() => {
    setCount(c => c + 1); // 此處的c就是最新的count
  }, 1000);
}, []);

注:此處定時器的清除我們放在useEffect(2.1)里單獨(dú)講解。

1.3、initState在常見場景下,我們通常作為state的初始值來使用,當(dāng)state的初始值過于復(fù)雜時,我們可能會依賴函數(shù)去處理。而這時候其實(shí)你應(yīng)該明白,useState并非只能接收基礎(chǔ)類型的數(shù)據(jù),而且也支持函數(shù)的接收。

// 錯誤:
const initState = () => {
  //...
  return '';
};
const [state, setState] = useState(initState());
// 正確:
const [state, setState] = useState(initState);

注:錯誤示例中,useState接收的是initState函數(shù)返回的值,這樣的寫法會在組件每次更新(state或props更新時)都會觸發(fā)initState函數(shù)的執(zhí)行。正確的寫法是在useState里傳入函數(shù),在這個函數(shù)里做復(fù)雜計算后返回你需要的初始值。

2、useEffect

componentDidMount, componentDidUpdate, componentWillUnmount:useEffect Hook 可以表達(dá)所有這些(包括 不那么 常見 的場景)的組合。

getSnapshotBeforeUpdate,componentDidCatch 以及 getDerivedStateFromError:目前還沒有這些方法的 Hook 等價寫法,但很快會被添加。

useEffect(() => {
  //...
  return () => {
    //...
  }
}, []);

2.1、useEffect的第一個參數(shù)為函數(shù),每次useEffect觸發(fā)時會執(zhí)行這個函數(shù),在這個函數(shù)中支持return出一個函數(shù),通常我們會在這個副作用來模擬componentWillUnmount,??但是請注意:這時候你應(yīng)該用useEffect的第二個參數(shù)傳入空數(shù)組去配合。

// 錯誤:
const ref = useRef();
const [count, setCount] = useState(0);
useEffect(() => {
  ref.current = setTimeout(() => setCount(c => c + 1), 1000);
  return () => {
    // 此處在每次count更新后都會觸發(fā)執(zhí)行
    clearTimeout(ref.current)
  };
}, [count]);
// 正確:
const ref = useRef();
const [count, setCount] = useState(0);
useEffect(() => {
  ref.current = setTimeout(() => setCount(c => c + 1), 1000);
}, [count]);
useEffect(() => {
  return () => {
    // 此處僅在組件銷毀前執(zhí)行
    clearTimeout(ref.current)
  };
}, []);

注:此處僅僅為demo需要,正常開發(fā)中setTimeout換成setInterval效果更佳。
參考如下:
const ref = useRef();
const [count, setCount] = useState(0);
useEffect(() => {
  ref.current = setInterval(() => setCount(c => c + 1), 1000);
  return () => {
    clearInterval(ref.current)
  };
}, []);

2.2、useEffect的第二個參數(shù)為非必填值數(shù)組。當(dāng)?shù)诙€參數(shù)不傳時,組件每次更新(state或props更新時)都會觸發(fā)第一個入?yún)⒑瘮?shù)的執(zhí)行,effect內(nèi)部的props和state也會隨之更新。

useEffect(() => {
  // 此處在組件每次更新(state或props更新時)都會觸發(fā)執(zhí)行
  // ...
});

2.3、當(dāng)?shù)诙€參數(shù)傳入空數(shù)組時,僅在組件掛載后觸發(fā)第一個入?yún)⒑瘮?shù)的執(zhí)行,而且effect內(nèi)部的props和state就會一直持有其初始值。

useEffect(() => {
  // 此處僅在組件掛載后觸發(fā)執(zhí)行
  // ...
}, []);

2.4、當(dāng)?shù)诙€參數(shù)傳入非空數(shù)組時,組件掛載后和數(shù)組內(nèi)部參數(shù)更新后都會觸發(fā)第一個入?yún)⒑瘮?shù)的執(zhí)行,且effect內(nèi)部的props和state會隨著監(jiān)聽值變化而更新。

const ref = useRef();
const [count, setCount] = useState(0);
useEffect(() => {
  // 每次count更新后,都會再次觸發(fā)執(zhí)行
  ref.current = setTimeout(() => setCount(c => c + 1), 1000);
}, [count]);

注:實(shí)際上,useEffect的第二個參數(shù)數(shù)組的每項中,支持對Object的監(jiān)聽。這樣在實(shí)際開發(fā)中,我們可以對部分關(guān)聯(lián)性較強(qiáng)的state做統(tǒng)一化處理:
const [payload, setPayload] = useState({
  searchKey: undefined,
  type: undefined,
  pageIndex: 1,
  pageSize: 10,
  // ...
});
useEffect(() => {
  dispatch({ type: 'xxx', payload });
}, [payload]);
// 為了方便payload狀態(tài)的更新,我們可以這樣:
const updatePayload = (data) => setPayload((d) => ({ ...d, ...data }));

注:當(dāng)state的狀態(tài)過于復(fù)雜且包含多個值時,建議使用useReducer。

3、useRef

const ref = useRef(initialValue);

3.1、useRef返回一個可變的ref對象,其.current屬性則為初始化時傳入的參數(shù)initialValue。通常我們會用其獲取dom元素,但是由于其內(nèi)置屬性.current是一個可變值的“盒子”,利用其整個生命周期都持續(xù)存在的特性,可以實(shí)現(xiàn)在class函數(shù)中this.timer的寫法去保存某些值。(比如:定時器的序號)

const ref = useRef();
const [count, setCount] = useState(0);
useEffect(() => {
  ref.current = setInterval(() => setCount(c => c + 1), 1000);
  return () => {
    clearInterval(ref.current)
  };
}, []);

注:若要將ref暴露給父元素,可以使用useImperativeHandle。
此文會不定時更新,若對HOOK有興趣請點(diǎn)贊關(guān)注或留言,大家一起探討。??
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容