<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【每日一題NO.66】useEffect、useCallback、useMemo理解及仿寫

          共 4726字,需瀏覽 10分鐘

           ·

          2021-10-25 04:57

          useEffect

          useEffect 一般用于處理狀態(tài)更新導(dǎo)致的副作用

          useEffect 可以看做是 componentDidMount/componentDidUpdate/componentWillUnmount 這三個生命周期函數(shù)的替代。

          語法:

          useEffect(didUpdate,?deps);

          didUpdate

          是一個包含命令式并且可能會有副作用代碼的函數(shù),是組件渲染成功并且 deps 依賴參數(shù)發(fā)生變化時(shí)執(zhí)行的函數(shù)。該函數(shù)可以沒有返回值,只是執(zhí)行內(nèi)部的內(nèi)容。

          但是當(dāng) didUpdate 有返回值的時(shí)候,返回值必須是一個可執(zhí)行的函數(shù),目的是用于清除 didUpdate 執(zhí)行過程中產(chǎn)生的訂閱或者計(jì)數(shù)器ID等資源。

          如果 didUpdate 多次觸發(fā),則在每次重新執(zhí)行前都會先行返回的可執(zhí)行函數(shù),官方稱之為清除effect

          以下是官網(wǎng)提供的例子,可以全面展示 useEffect 的使用方式:

          import?React,?{?useState,?useEffect?}?from?"react";

          //?該組件定時(shí)從服務(wù)器獲取好友的在線狀態(tài)
          function?FriendStatus(props)?{
          ??const?[isOnline,?setIsOnline]?=?useState(null);

          ??useEffect(()?=>?{
          ????function?handleStatusChange(status)?{
          ??????setIsOnline(status.isOnline);
          ????}
          ????//?在瀏覽器渲染結(jié)束后執(zhí)行
          ????ChatAPI.subscribeToFriendStatus(props.friend.id,?handleStatusChange);

          ????//?在每次渲染產(chǎn)生的?effect?執(zhí)行之前執(zhí)行
          ????return?function?cleanup()?{
          ??????ChatAPI.unsubscribeFromFriendStatus(props.friend.id,?handleStatusChange);
          ????};

          ????//?只有?props.friend.id?這個依賴更新了才會重新執(zhí)行這個?hook
          ??},?[props.friend.id]);

          ??if?(isOnline?===?null)?{
          ????//繼續(xù)等待
          ????return?"Loading...";
          ??}
          ??return?isOnline???"Online"?:?"Offline";
          }

          仿寫useEffect

          let?hookStates?=?[];?//?保存狀態(tài)的數(shù)組?[0,0]
          let?hookIndex?=?0;?//?索引
          function?useEffect(callback,dependencies){
          ??if(hookStates[hookIndex]){
          ????//?說明不是第一次執(zhí)行
          ????let?lastDependencies?=?hookStates[hookIndex];
          ????let?same?=?dependencies.every((item,index)?=>?item?===?lastDependencies[index]);
          ????if(same){
          ??????hookIndex++;
          ????}else{
          ??????hookStates[hookIndex++]?=?dependencies;
          ??????callback();
          ????}
          ??}else{
          ????//?說明是第一次渲染
          ????hookStates[hookIndex++]?=?dependencies;?//?進(jìn)行依賴緩存
          ????callback();
          ??}
          }

          useLayoutEffect

          useEffect 是官方推薦拿來代替componentDidMount/componentDidUpdate/componentWillUnmount這三個生命周期函數(shù)的,但是它們并不是完全等價(jià)的:

          • useEffect 是在瀏覽器渲染結(jié)束之后才執(zhí)行的,
          • 這三個生命周期函數(shù)是在瀏覽器渲染之前同步執(zhí)行的。

          而能夠完美等價(jià)這三個生命周期函數(shù)的另一個官方hook就是 useLayoutEffect。

          useEffect 是在瀏覽器重繪之后才異步執(zhí)行的,而 useLayoutEffect 是在瀏覽器重繪之前同步執(zhí)行的。

          因?yàn)?useEffect 不會阻塞瀏覽器重繪,而且平時(shí)業(yè)務(wù)中遇到的絕大多數(shù)場景都是時(shí)機(jī)不敏感的,比如取數(shù)、修改 dom、事件觸發(fā)/監(jiān)聽…,所以首先推薦使用 useEffect 來處理 副作用,性能上表現(xiàn)的更好一些。

          useEffect(()?=>?{
          ??const?timer?=?setInterval(()?=>?{
          ????console.log("timer");
          ??},?1000);
          ??return?()?=>?{
          ????//?清除定時(shí)器
          ????clearInterval(timer);
          ??};
          });

          useCallback

          語法:

          const?memoizedCallback?=?useCallback(()?=>?{
          ??doSomething(params);
          },?deps);

          deps 是依賴的參數(shù)列表,當(dāng)依賴列表中的任一參數(shù)變化時(shí),則重新執(zhí)行前面的函數(shù)

          返回一個 memoize回調(diào)函數(shù),即返回一個函數(shù)的句柄,等同于函數(shù)的變量,因此 useCallback 的作用在于利用 memoize 減少無效的 re-render 來達(dá)到性能優(yōu)化的作用。

          有人會誤以為 useCallback 可以用來解決創(chuàng)建函數(shù)造成的性能問題,其實(shí)恰恰相反。單個組件來看,useCallback 只會更慢,因?yàn)?inline 函數(shù)是無論如何都會創(chuàng)建的,還增加了 useCallback 內(nèi)部對 inputs 變化的檢測。

          useCallback 的真正目的是在于緩存每次渲染時(shí) inline callback 的實(shí)例,這樣方便配合上子組件的 shouldComponentUpdate 或者 React.memo 起到減少不必要渲染的作用,需要注意的是 React.memoReact.useCallback 一定要配對使用。缺了一個可能導(dǎo)致性能不升反降。畢竟無意義的淺比較也是消耗那么一點(diǎn)點(diǎn)性能。

          仿寫

          let?hookStates?=?[];?//?保存狀態(tài)的數(shù)組?[0,0]
          let?hookIndex?=?0;?//?索引
          function?useCallback(callback,?dependencies)?{
          ??if?(hookStates[hookIndex])?{?//?說明不是第一次,
          ????let?[lastCallback,?lastDependencies]?=?hookStates[hookIndex];
          ????//?判斷一下新的依賴數(shù)組中的每一項(xiàng)是否跟上次完全相等
          ????let?same?=?dependencies.every((item,?index)?=>?item?===?lastDependencies[index]);
          ????if?(same)?{
          ??????hookIndex++;
          ??????return?lastCallback;
          ????}?else?{?//?只要有一個依賴變量不一樣的話
          ??????hookStates[hookIndex++]?=?[callback,?dependencies];?//?hookIndex=3?callback=()=>setNumber(number+1)??dependencies=[0]
          ??????return?callback;
          ????}
          ??}?else?{?//?說明是第一次渲染
          ????hookStates[hookIndex++]?=?[callback,?dependencies];?//?hookIndex=3?callback=()=>setNumber(number+1)??dependencies=[0]
          ????return?callback;
          ??}
          }

          useMemo

          語法:

          const?memoizedValue?=?useMemo(()?=>?computerExpensiveValue(params),?deps);

          返回一個 memoize useMemo 函數(shù)每當(dāng) deps 發(fā)生變化時(shí)都會調(diào)用 componentExpensiveValue 的內(nèi)容,這是和 useCallback 最大的不同,useCallback 不執(zhí)行 dosomething 的內(nèi)容,只是重新刷新函數(shù)句柄。

          官方文檔上有這樣的一個等式:

          useCallback(fn, deps) 相當(dāng)于 useMemo(() => fn, deps)。

          當(dāng) deps 發(fā)生變化時(shí),useCallback 返回的值是一個可執(zhí)行的 fn 的句柄,而 useMemo 則是執(zhí)行()=>fn

          useMemo 是拿來保持一個對象引用不變的。useMemouseCallback 都是 React 提供來做性能優(yōu)化的。比起 class,Hooks 給開發(fā)者更高的靈活度和自由,但是對開發(fā)者要求也更高了,因?yàn)?Hooks 使用不當(dāng)很容易導(dǎo)致性能問題。

          仿寫

          let?hookStates?=?[];?//?保存狀態(tài)的數(shù)組?[0,0]
          let?hookIndex?=?0;?//?索引
          function?useMemo(factory,?dependencies)?{
          ??if?(hookStates[hookIndex])?{?//?說明不是第一次,
          ????let?[lastMemo,?lastDependencies]?=?hookStates[hookIndex];
          ????//?判斷一下新的依賴數(shù)組中的每一項(xiàng)是否跟上次完全相等
          ????let?same?=?dependencies.every((item,?index)?=>?item?===?lastDependencies[index]);
          ????if?(same)?{
          ??????hookIndex++;
          ??????return?lastMemo;
          ????}?else?{?//?只要有一個依賴變量不一樣的話
          ??????let?newMemo?=?factory();
          ??????hookStates[hookIndex++]?=?[newMemo,?dependencies];
          ??????return?newMemo;
          ????}
          ??}?else?{?//?說明是第一次渲染
          ????let?newMemo?=?factory();
          ????hookStates[hookIndex++]?=?[newMemo,?dependencies];
          ????return?newMemo;
          ??}
          }

          所有《每日一題》的 知識大綱索引腦圖 整理在此:https://www.yuque.com/dfe_evernote/interview/everyday
          你也可以點(diǎn)擊文末的 “閱讀原文” 快速跳轉(zhuǎn)


          END
          愿你歷盡千帆,歸來仍是少年。


          瀏覽 65
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  一级黄色H片在线播放 | 18禁免费观看网站 | 西西444www大胆高清图片 | 美女全裸网站91网视麻豆 | 黄色成人在线免费 |