<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【每日一面】React Hooks閉包陷阱

      基礎(chǔ)問答

      問題:談一談你對 React Hook的閉包陷阱的理解。

      產(chǎn)生問題的原因:JavaScript 閉包特性 + Hooks 渲染機制

      閉包的本質(zhì):函數(shù)能夠訪問其定義時所在的詞法作用域,即使函數(shù)在作用域外執(zhí)行,也可以記住定義時的詞法作用域的內(nèi)容,后續(xù)執(zhí)行時,使用這些信息。

      function callback(index) {
        let idx = index;
        let op;
        return (type) => {
          op = type;
          console.log(op);
          switch (type) {
            case 'add':
              idx++;
              break;
            case 'sub':
              idx--;
              break;
          }
          return idx;
        }
      }
      
      const fn = callback(8);
      console.log(fn('add')); // 9
      console.log(fn('sub')); // 8
      

      這里的 idx 正常會在 callback 函數(shù)執(zhí)行結(jié)束后釋放,但是由于我們返回的是一個函數(shù),函數(shù)中依賴這個 idx 變量,所以未能釋放,此時這個變量被這個匿名函數(shù)持有,而在 fn 變量存續(xù)期間,idx 和 op 都是不會釋放的,這也就形成了一個閉包。

      不過經(jīng)典閉包還是 for 循環(huán)

      Hooks 渲染邏輯:React 組件每次渲染都是獨立的快照,可以理解為,每次重新執(zhí)行相關(guān)鉤子的時候,組件都會重新生成一個新的作用域。

      閉包陷阱:根據(jù)上面兩點,React Hooks 的閉包陷阱產(chǎn)生過程應當是這樣的,React 在渲染開始前創(chuàng)建了新的狀態(tài)包(作用域),而我們寫代碼的時候無意中創(chuàng)建了一個閉包,持有了 React 的當前狀態(tài),再下次渲染開始時,React 重新創(chuàng)建了狀態(tài)包,但是我們在一開始創(chuàng)建的閉包持有的依舊是前一次 React 創(chuàng)建的狀態(tài),是舊的,這就是產(chǎn)生閉包陷阱的根源。這里我們以一個具體例子來看:

      import { useEffect, useState } from "react"
      
      const App = () => {
        const [count, setCount] = useState(1);
      
        useEffect(()=> {
          const timer = setInterval(() => console.log(count), 1000);
          return () => clearInterval(timer)
        }, []);
      
        const addOne = () => {
          setCount(pre => pre+1);
        }
      
        return (
          <div className="main" > 
            <p>Hello: {count}</p>
            <button onClick={addOne}>+1</button>
          </div>
        )
      }
      
      export default App
      
      

      這里在組件首次渲染的時候,useEffect 幫我們設(shè)置了一個定時器,定時器執(zhí)行的函數(shù)持有了外部作用域的 count 變量,產(chǎn)生了一個閉包。

      再之后,我們在頁面上點擊按鈕時,觸發(fā)了 setCount(pre => pre+1) 狀態(tài)更新,但是由于沒有配置 useEffect 的更新依賴,所以定時器還是持有舊的狀態(tài)包。此時打印的還是 1,沒有更新。

      閉包陷阱破解方式

      1. 使用 useRef:useRef 在初始化后,是一個形如 { current: xxx } 的不可變對象,不可變可以理解為,這個對象的地址不會發(fā)生變化,所以在淺層次的比較(===)中,更新后的前后對象是一個。所以取值的時候,總是能拿到最新的值。
      2. 添加 Hooks 依賴:在 useEffect 鉤子的依賴列表中增加 count,當 count 發(fā)生變化的時候,會重新執(zhí)行 useEffect ,內(nèi)部的 timer 會重新生成,拿到最新的作用域的值。
      3. 修改 state 為一個對象:類似于 useRef,我們在更新 state 的時候,可以直接把內(nèi)容寫入該對象中,避免直接替換 state 對象。

      擴展知識

      React 官方要求我們不能將 hooks 用 if 條件判斷包裹,其原因是 React 的 Fiber 架構(gòu)中收集 Hooks 信息的時候是按順序收集的,并以鏈表的形式進行存儲的。如下示例:

      function App() {
        const [count, setCount] = useState(0);
        const [isFirst, setIsFirst] = useState(false);
      
        useEffect(() => {
          console.log('hello init');
        }, []);
      
        useEffect(() => {
          console.log('count change: ', count);
        }, [count]);
      
        const a = 1;
      }
      

      示例中存在 4 個 hooks,所以 React 收集完成后形成的鏈表應當是這樣的:

      鏈表圖

      React 為鏈表節(jié)點設(shè)計了如下數(shù)據(jù)結(jié)構(gòu):

      type Hook = {
        memoizedState: any,
        /** 省略這里不需要的內(nèi)容 */
        next: Hook | null,
      };
      

      其中 next 就是鏈表節(jié)點用于指向下一個節(jié)點的指針,memoizedState 則是上一次更新后的相關(guān) state。組件更新的時候,hooks 會嚴格按照這個順序進行執(zhí)行,按順序拿到對應的 Hook 對象,所以如果我們用 if else 包裹了其中一個 hook,就會出現(xiàn)鏈表執(zhí)行過程中,Hooks 對象取值錯誤的情況。

      同樣的,React 官方告訴我們,如果想在更新的時候拿到當前 state 的值,建議使用回調(diào)函數(shù)的寫法,即:setCount(pre => pre + 1) 這種寫法,這個原因,通過 Hook 的數(shù)據(jù)結(jié)構(gòu)也大致可以判斷,因為 memoizedState 存儲了前一次更新的數(shù)據(jù),使用回調(diào)時,這個 memoizedState 就可以作為參數(shù)提供給我們,并且保證總是正確的。

      面試追問

      1. 能手寫一個閉包嗎?

      參考前文代碼。

      1. 使用 useRef 存儲值,會有什么問題?

      useRef 在初始化后,是形如 { current: xxx } 的對象,這個對象地址不會變化,所以我們監(jiān)聽 ref 是不起作用的,同時,和 useState 不同,useRef 內(nèi)容的變更不會觸發(fā)組件重新渲染。

      1. 請談談 hooks 在 React 中的更新邏輯?

      React 是以鏈表形式來組織管理 hooks 的,在收集過程中按照順序組裝成鏈表,然后每次觸發(fā)狀態(tài)更新時,會從鏈表頭開始依次判斷執(zhí)行更新。

      1. 那 hooks 中,useState 的更新是同步還是異步?

      可以理解為異步的,展開來說,則是: state 更新函數(shù)(如觸發(fā) setCount)是同步觸發(fā)的,React 執(zhí)行更新(即 count 被更新)是異步的。這種設(shè)計主要是出于性能考慮,避免重復渲染,減少重繪重排。

      1. useEffect 依賴數(shù)組傳空數(shù)組和不傳依賴,二者有什么區(qū)別?

      空數(shù)組:effect 僅在組件首次渲染時執(zhí)行一次,后續(xù)不會再執(zhí)行,相當于組件掛載階段。

      不傳依賴:effect 會在組件首次渲染時、每次重新渲染后都執(zhí)行。這種形式隱含存在渲染循環(huán)的風險,即 effect 中存在修改 state 的操作,那么按照不傳依賴時執(zhí)行的規(guī)則,就會陷入渲染 -> 更新 -> 觸發(fā)重渲染 -> 更新 -> 觸發(fā)重渲染……這樣的循環(huán)。

      posted @ 2025-09-26 16:44  Achieve前端實驗室  閱讀(169)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 国产精品国产精品偷麻豆| 私人毛片免费高清影视院| 元码人妻精品一区二区三区9| 精品国偷自产在线视频99| 人妻日韩人妻中文字幕| 铁力市| 狠狠亚洲超碰狼人久久| 久久综合九色综合97婷婷| 精品国产中文字幕在线| 国产999久久高清免费观看| 亚洲精品天堂在线观看| 少妇人妻偷人精品无码视频| 亚洲精国产一区二区三区| 久久精品亚洲日本波多野结衣| 少妇愉情理伦片丰满丰满午夜| 精品中文人妻中文字幕| 欧美福利电影A在线播放| 成人无码午夜在线观看| 国产亚洲精品久久久久婷婷图片 | 国产成人AV国语在线观看| 精品偷拍一区二区三区| 亚洲精品香蕉一区二区| 亚洲精品麻豆一二三区| 日韩中文字幕亚洲精品一| 在线精品国产中文字幕| av天堂久久精品影音先锋 | 亚洲午夜精品久久久久久抢| 国产色悠悠综合在线观看| 亚洲V天堂V手机在线| 92成人午夜福利一区二区| 中文字幕熟妇人妻在线视频| 91精品久久一区二区三区| 亚洲精品国产综合久久一线| 国产热の有码热の无码视频| 天堂影院一区二区三区四区| 精品国产免费人成网站| 国产初高中生粉嫩无套第一次 | 色综合天天综合天天综| 国产精品入口中文字幕| 美女裸体18禁免费网站| 亚洲精品免费一二三区|