1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【每日一題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ā)生變化時執(zhí)行的函數(shù)。該函數(shù)可以沒有返回值,只是執(zhí)行內(nèi)部的內(nèi)容。

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

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

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

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

        //?該組件定時從服務(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ù)的,但是它們并不是完全等價的:

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

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

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

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

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

        useCallback

        語法:

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

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

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

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

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

        仿寫

        let?hookStates?=?[];?//?保存狀態(tài)的數(shù)組?[0,0]
        let?hookIndex?=?0;?//?索引
        function?useCallback(callback,?dependencies)?{
        ??if?(hookStates[hookIndex])?{?//?說明不是第一次,
        ????let?[lastCallback,?lastDependencies]?=?hookStates[hookIndex];
        ????//?判斷一下新的依賴數(shù)組中的每一項是否跟上次完全相等
        ????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ā)生變化時都會調(diào)用 componentExpensiveValue 的內(nèi)容,這是和 useCallback 最大的不同,useCallback 不執(zhí)行 dosomething 的內(nèi)容,只是重新刷新函數(shù)句柄。

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

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

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

        useMemo 是拿來保持一個對象引用不變的。useMemouseCallback 都是 React 提供來做性能優(yōu)化的。比起 class,Hooks 給開發(fā)者更高的靈活度和自由,但是對開發(fā)者要求也更高了,因為 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ù)組中的每一項是否跟上次完全相等
        ????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
        你也可以點擊文末的 “閱讀原文” 快速跳轉(zhuǎn)


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


        瀏覽 65
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            国产成人精品久久 | 久久精品日本啪啪涩涩 | 国语对白xxxx中国妞xxxx | 无码破解一区二区三区在线播报 | 好大好硬好想要 | 99精品久久久久久中文字幕 | 欧美与黑人午夜性猛交久久久 | 久久豆花 | 国产免费观看性爱网站 | 日本亚洲精品色婷婷在线影院 |