🎉

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

2022/11/19に公開

サンプルコード

import React, { useRef, useState } from 'react';

const App = () => {

    const obj = useRef({
        name:"villa",
    })

    const [count,setCount] = useState(0)

    const handleClick = () => {
        setCount(c => c + 1)
    }


   return (
      <div className='container'>
        <p>{obj.current.name}: {count}</p>
        <button onClick={handleClick}>click</button>
    </div>
   );
};


export default App

マウント段階

  1. サンプルコードの以下の行を実行します
const obj = useRef({name:"villa"})
  1. useRefの内部を呼びます

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

ここのinitValueは{name:"villa"}です

  1. dispatcherのuseRefを呼びます

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

  1. 内容をhookに保存します

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

これ見たら、{current: 保存したいオブジェクト}というデータ構造になります、だから、取り出す時には、objではなく、obj.currentで書く必要があります。

更新段階

  1. ボタンをクリックし、再レンダリングを走らせます

  2. サンプルコードの以下の行を実行します

const obj = useRef({name:"villa"})

ここはマウント段階と同じです。

  1. useRefの内部を呼びます

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

ここはマウント段階と同じです。

  1. dispatcherのuseRefを呼びます

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

  1. updateRefを呼びます。

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

ここからみたら、マウント段階のhook.memoizedStateをなにも触らず、そのまま取り出しています。
useRefを囲んた値はマウント段階でhookに保存してから、ずっと変わらないことをわかりました。

Discussion