🥑

React useCallbackの内部ソースコードを読んでみました

2022/11/11に公開

コード

import { useCallback, useState } from 'react';

const App = () => {
    const [dataInvolved, setDataInvolved] = useState(1)
    const [dataNotInvolved, setDataNotInvolved] = useState(1)

    const nums = useCallback(() => dataInvolved * dataInvolved, [dataInvolved])
    const handleClick = () => {
        setDataInvolved(d => d + 1)
    }

    const handleClick2 = () => {
        setDataNotInvolved(d => d + 1)
    }

   return (
       <div className='container'>
        <button onClick={handleClick}>click</button>
        <button onClick={handleClick2}>click2</button>
        <p>count:{nums()}</p>
       </div>
   );
};
export default App

動作

  • clickボタンをクリックした時に、count:{値変化あり}
  • click2ボタンをクリックした時に、count:{値変化ありません}

流れ説明

mount時

  1. App関数を実行、以下の行を実行します
const nums = useCallback(() => dataInvolved * dataInvolved, [dataInvolved])
  1. react内部のuseCallbackを実行します

https://github.com/facebook/react/blob/main/packages/react/src/ReactHooks.js#L150

  1. mountCallbackを実行します

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2854

  1. Hookオブジェクト作成します

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2171

  1. 作成したオブジェクトをAppのFiberNodeに保存します

https://github.com/facebook/react/blob/1e3e30dae2bcfbeb0abc686f2a37aec208eedb39/packages/react-reconciler/src/ReactFiberHooks.new.js#L790

  1. 関数とdepsを作成したhookオブジェクトに保存します

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2173

7.callback関数をAppに返します

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2174

  1. App関数をcallback関数をもって、実行、最新のReact.Elementを作成し、Fiberツリー構築へ
  2. dom操作して、画面に描画させます

update時

  1. App関数を実行、以下の行を実行します(mount時と同じです)
  2. react内部のuseCallbackを実行します(mount時と同じです)
  3. updateCallbackを実行します

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L3170

  1. Hookオブジェクト作成します(mount時と同じです)
  2. 作成したオブジェクトをAppのFiberNodeに保存します(mount時と同じです)
  3. AppのFiberNodeに保存してcallbackとdepsを取り出します

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2180

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2183

  1. 今渡しているdepsとAppのFiberNodeに保存されたdepsとshallow equalします

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2184

8-1. 7の結果はtrueの場合は、記憶したcallbackを返します

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2185

8-2. 7の結果はfalseの場合は、記憶を更新して、渡されたcallback関数をAppに返します。

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberHooks.new.js#L2189-L2190

  1. App関数をcallback関数をもって、実行、最新のReact.Elementを作成し、Fiberツリー構築へ
  2. 更新がある場合、dom操作して、画面に描画させます

メモリ図

https://viewer.diagrams.net/?tags={}&highlight=0000ff&edit=_blank&layers=1&nav=1&page-id=TBl-Up2SRTjNr80fJSiz&title=useMemo useCallback.drawio#Uhttps%3A%2F%2Fdrive.google.com%2Fuc%3Fid%3D1aEf64Maa2jyRdkZZw7fqB268DMwYU-4I%26export%3Ddownload

説明動画

https://www.youtube.com/watch?v=uV6Ont_a_I4

Discussion