🥦

React.memoの内部ソースコードを読んでみました(本当に使うべきかを見極め)

2022/11/12に公開

React.memoの公式説明

https://ja.reactjs.org/docs/react-api.html#reactmemo

公式の説明は以下ですね

ソースコードにReact.memoが効くための判断条件

判断条件

https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberBeginWork.new.js#L596-L641

以下の二つの条件を満たしたら、React.memoで囲んだComponentを走りません

  1. 渡されたpropsとメモしているpropsとのshallow equalはtrueになっています。
    これは公式の説明の以下の分に当てはまります

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

  1. Componentの中にはContextの変化がないこと
    これは公式の説明の以下の分に当てはまります。

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

判断結果

1. 効果がある場合

1&2を満たしたら、以下の処理に入ります

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

つまり、React.memoで囲んだComponentに対して、子FiberNodeの更新がないことと想定され、
前のレンダリング段階の子FiberNodeをそのまま使います。
もちろん、React.memoで囲んだComponentは関数として走りません。(子FiberNodeの遺伝子React.Elementを作り出すために、関数を実行するわけです)

2. 効果がない場合

1&2を満たしなかったら、以下の処理に入ります
https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberBeginWork.new.js#L648

つまり、React.memoで囲んだComponentを実行して、子FiberNodeの新しいReact.Elementを生成して、現在のFiberNodeの子FiberNodeのpendingPropsを更新します。

React.memoを使うメリットとデメリット

メリット

  1. 頭を考えずに、Reactレンダリング時にReact.memoで囲んだ関数実行を最大限に抑えられます。

デメリット

  1. React.memoに対するひとつFiberNodeを増えています。(メモリのコスト)
  2. propsに対するshallowEqual処理(時間コスト)
  3. contextの変化があるかどうかの判断(時間コスト、なかにはwhile分があります)

使うかどうかの個人意見

ネット上ではReact.memoを使うかどうかの意見はあちこちにありますが、まよいますね。

もし囲んでいるComponentにはuseState,useReducer,useContext,useSelector(redux)
たくさんあって、そして、頻繁によばれるなら、使う意味はあまりないと思います。

なぜかというと、いくらuseMemoとuseCallbackをつかって、propsをメモして、第1条件を満たしても、第2条件はuseStateなどの更新によって、満たされてないなら、結局React.memoが囲んだComponentが走ります。
そうなると、無駄な時間コストとメモリのコストになります。

説明動画

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

Discussion