欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解react中useCallback內(nèi)部是如何實(shí)現(xiàn)的

 更新時(shí)間:2023年07月04日 09:11:32   作者:AKclown  
前幾天有人在問(wèn)在useCallback函數(shù)如果第二個(gè)參數(shù)為空數(shù)組, 為什么拿不到最新的state值,那么這一章就來(lái)分析一下useCallback內(nèi)部是如何實(shí)現(xiàn)的,感興趣的小伙伴跟著小編一起來(lái)學(xué)習(xí)吧

示例demo與debug

新建了一個(gè)react項(xiàng)目,將APP.tsx改寫(xiě)成如下代碼

import  { useCallback, useState } from 'react';
function App() {
  const [num, updateNum] = useState(0);
  const TestCallback  = useCallback(() =>{
    console.log('num: ', num);
  },[]);
  return (
    <div className="App">
        <p onClick={() => {
          updateNum(num => num + 1);
          updateNum(num => num + 1);
          updateNum(num => num + 1);
        }}>{num}</p>
        <p onClick={TestCallback}>打印</p>
    </div>
  );
}
export default App;

在瀏覽器的source設(shè)置斷點(diǎn),熟悉一遍useCallback的調(diào)用流程。(由于.gif過(guò)大,這里就不上git了,自行調(diào)試)

源碼解析

useCallback的整體流程框架

在react中mount階段和update階段進(jìn)入到同一個(gè)useCallback方法里。但resolveDispatcher找到的dispatch對(duì)象mountupdate會(huì)不同,最終導(dǎo)致在mount階段調(diào)用mountCallback而update階段調(diào)用的是updateCallback。
下面為調(diào)用useCallback方法觸發(fā)的行為

function useCallback(callback, deps) {
  var dispatcher = resolveDispatcher();
  return dispatcher.useCallback(callback, deps);
}

下面來(lái)看看resolveDispatcher是如何獲取到dispatch的

function resolveDispatcher() {
  const dispatcher = ReactCurrentDispatcher.current;
  ...
  return ((dispatcher: any): Dispatcher);
}

ReactCurrentDispatcher.current會(huì)在renderWithHooks方法中進(jìn)行所處階段判斷并且賦值。如果current === null || current.memoizedState === null為true表示在mount階段反正為update階段

function renderWithHooks<Props, SecondArg>(...) {
     ...
     ReactCurrentDispatcher.current =
      current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
}
// mount階段調(diào)用的dispatch
const HooksDispatcherOnMount: Dispatcher = {
  ...
  useCallback: mountCallback,
};
// update階段調(diào)用的dispatch
const HooksDispatcherOnUpdate: Dispatcher = {
  ...
  useCallback: updateCallback,
};

從上面的代碼分析可以知道在mounted階段調(diào)用的是mountCallbackupdate階段調(diào)用updateCallback

Hook

一個(gè)函數(shù)式組件鏈路: fiber(FunctionComponent) => Hook(保存數(shù)據(jù)狀態(tài)) => Queue(更新的隊(duì)列結(jié)構(gòu)) => update(更新的數(shù)據(jù))在后續(xù)需要使用到Hook這個(gè)結(jié)構(gòu),那么先來(lái)看一下Hook是數(shù)據(jù)結(jié)構(gòu)是怎么樣的,以及屬性的作用是什么?

  • memoizedState 存放的是Hook對(duì)應(yīng)的state
  • next鏈接到下一個(gè)Hook,從而形成一個(gè)無(wú)環(huán)單向鏈表
  • queue存儲(chǔ)同一個(gè)hook更新的多個(gè)update對(duì)象,數(shù)據(jù)結(jié)構(gòu)為環(huán)狀單向鏈表
// 組件對(duì)應(yīng)的fiber對(duì)象
const fiber = {
  // 保存該FunctionComponent對(duì)應(yīng)的Hooks鏈表
  memoizedState: hook,
  ...
};
const hook: Hook = {
    // 1.  memoizedState 存放的是Hook對(duì)應(yīng)的state
    memoizedState: null,
    // 2. next鏈接到下一個(gè)Hook,從而形成一個(gè)`無(wú)環(huán)單向鏈表`
    queue: null,
    // 3. queue存儲(chǔ)同一個(gè)hook更新的多個(gè)update對(duì)象,數(shù)據(jù)結(jié)構(gòu)為`環(huán)狀單向鏈表`  
    next: null,
    ...
};

fiber與Hooks的關(guān)系(懶得畫(huà)圖了,引用了Understanding the Closure Trap of React Hooks)

mount階段

分析mountCallback的實(shí)現(xiàn)

  • 通過(guò)mountWorkInProgressHook獲取到對(duì)應(yīng)的Hook對(duì)象
  • 判斷條件deps是否為undefined
  • 回調(diào)函數(shù)判斷條件存入到hook.memoizedState
  • 返回傳入的回調(diào)函數(shù)
function mountCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  const hook = mountWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

mountWorkInProgressHook的實(shí)現(xiàn),創(chuàng)建初始化Hook對(duì)象,并且將該Hook對(duì)象保存在workInProgressHook鏈路中. workInProgressHook表示正在執(zhí)行的hook

function mountWorkInProgressHook(): Hook {
  const hook: Hook = {
    memoizedState: null,
    baseState: null,
    baseQueue: null,
    queue: null,
    next: null,
  };
  if (workInProgressHook === null) {
    // This is the first hook in the list
    currentlyRenderingFiber.memoizedState = workInProgressHook = hook;
  } else {
    // Append to the end of the list
    workInProgressHook = workInProgressHook.next = hook;
  }
  return workInProgressHook;
}

在組件render時(shí),每當(dāng)遇到下一個(gè)Hook,通過(guò)移動(dòng)workInProgressHook的指針來(lái)獲取到對(duì)應(yīng)的HookPS: 只要每次組件render時(shí)useState的調(diào)用順序及數(shù)量保持一致,那么始終可以通過(guò)workInProgressHook找到當(dāng)前useState對(duì)應(yīng)的hook對(duì)象

// fiber.memoizedState標(biāo)識(shí)第一個(gè)Hook
workInProgressHook = fiber.memoizedState;
// 在組件`render`時(shí),遇到下一個(gè)hook時(shí)
workInProgressHook = workInProgressHook.next;
....

update階段

分析updateCallback的實(shí)現(xiàn)

  • 通過(guò)updateWorkInProgressHook獲取到當(dāng)前的Hook對(duì)象
  • hook.memoizedState獲取到上一次緩存的state。假設(shè)這是第一次update那么其值就是mount階段保存的[callback, nextDeps]數(shù)據(jù)
  • 如果依賴(lài)條件不為空,使用areHookInputsEqual判斷依賴(lài)項(xiàng)是否更改。只會(huì)遍歷數(shù)組第一層數(shù)據(jù)比較不會(huì)做深層比較。如果依賴(lài)項(xiàng)沒(méi)變化,返回原本緩存的callback。
function updateCallback<T>(callback: T, deps: Array<mixed> | void | null): T {
  const hook = updateWorkInProgressHook();
  const nextDeps = deps === undefined ? null : deps;
  const prevState = hook.memoizedState;
  if (nextDeps !== null) {
    const prevDeps: Array<mixed> | null = prevState[1];
    if (areHookInputsEqual(nextDeps, prevDeps)) {
      return prevState[0];
    }
  }
  hook.memoizedState = [callback, nextDeps];
  return callback;
}

依賴(lài)比較areHookInputsEqual的方法實(shí)現(xiàn)

function areHookInputsEqual(
  nextDeps: Array<mixed>,
  prevDeps: Array<mixed> | null,
): boolean {
  ...
  // $FlowFixMe[incompatible-use] found when upgrading Flow
  for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
    // $FlowFixMe[incompatible-use] found when upgrading Flow
    if (is(nextDeps[i], prevDeps[i])) {
      continue;
    }
    return false;
  }
  return true;
}

總結(jié)

在React中會(huì)使用閉包機(jī)制來(lái)處理上文的callback回調(diào)函數(shù)。當(dāng)包含useCallback組件被渲染時(shí),React 會(huì)為該特定渲染周期創(chuàng)建一個(gè)閉包。閉包是一個(gè)封裝的作用域,其中包含渲染時(shí)位于作用域內(nèi)的變量、函數(shù)和其他引用
因此deps我們傳入的是空數(shù)組,其回調(diào)函數(shù)callback一直引用的狀態(tài)始終是初始狀態(tài),無(wú)法獲取最新?tīng)顟B(tài)。緩存的回調(diào)函數(shù)可以訪(fǎng)問(wèn)最初調(diào)用時(shí)范圍內(nèi)的狀態(tài)和道具

插件推薦

閱讀源碼可以通過(guò)使用Bookmarks快速標(biāo)記代碼位置,實(shí)現(xiàn)快速條件

到此這篇關(guān)于詳解react中useCallback內(nèi)部是如何實(shí)現(xiàn)的的文章就介紹到這了,更多相關(guān)react useCallback內(nèi)部實(shí)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React中Context的用法總結(jié)

    React中Context的用法總結(jié)

    Context?提供了一種在組件樹(shù)中共享數(shù)據(jù)的方式,而不必通過(guò)?props?顯式地逐層傳遞,主要用于共享那些對(duì)于組件樹(shù)中許多組件來(lái)說(shuō)是"全局"的數(shù)據(jù),下面小編就來(lái)和大家簡(jiǎn)單講講Context的用法吧
    2025-01-01
  • React?Hook中的useState函數(shù)的詳細(xì)解析

    React?Hook中的useState函數(shù)的詳細(xì)解析

    Hook 就是 JavaScript 函數(shù),這個(gè)函數(shù)可以幫助你鉤入(hook into) React State以及生命周期等特性,這篇文章主要介紹了React?Hook?useState函數(shù)的詳細(xì)解析的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • react類(lèi)標(biāo)簽的生命周期詳解

    react類(lèi)標(biāo)簽的生命周期詳解

    在React類(lèi)組件中,生命周期方法是非常重要的概念,它們?cè)试S我們?cè)诮M件的不同階段執(zhí)行代碼,這包括在組件掛載、更新以及卸載時(shí)執(zhí)行的生命周期方法,本文通過(guò)介紹React類(lèi)組件中的生命周期方法,旨在幫助開(kāi)發(fā)者深入理解組件的生命周期管理
    2024-11-11
  • React Navigation 路由傳參的操作代碼

    React Navigation 路由傳參的操作代碼

    這篇文章主要介紹了React Navigation 路由傳參,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • React高階組件使用教程詳解

    React高階組件使用教程詳解

    高階組件就是接受一個(gè)組件作為參數(shù)并返回一個(gè)新組件(功能增強(qiáng)的組件)的函數(shù)。這里需要注意高階組件是一個(gè)函數(shù),并不是組件,這一點(diǎn)一定要注意,本文給大家分享React 高階組件HOC使用小結(jié),一起看看吧
    2022-12-12
  • 深入理解React Native原生模塊與JS模塊通信的幾種方式

    深入理解React Native原生模塊與JS模塊通信的幾種方式

    本篇文章主要介紹了深入理解React Native原生模塊與JS模塊通信的幾種方式,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-07-07
  • React?中state與props更新深入解析

    React?中state與props更新深入解析

    這篇文章主要為大家介紹了React?中state與props更新深入解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • React Router 如何使用history跳轉(zhuǎn)的實(shí)現(xiàn)

    React Router 如何使用history跳轉(zhuǎn)的實(shí)現(xiàn)

    這篇文章主要介紹了React Router 如何使用history跳轉(zhuǎn)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • React如何利用Antd的Form組件實(shí)現(xiàn)表單功能詳解

    React如何利用Antd的Form組件實(shí)現(xiàn)表單功能詳解

    這篇文章主要給大家介紹了關(guān)于React如何利用Antd的Form組件實(shí)現(xiàn)表單功能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 關(guān)于react中列表渲染的局部刷新問(wèn)題

    關(guān)于react中列表渲染的局部刷新問(wèn)題

    這篇文章主要介紹了關(guān)于react中列表渲染的局部刷新問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08

最新評(píng)論