useEffect()の第2引数の差分検出はdeepではない
内容はタイトルで尽きてます。 気になって調べたのでメモを残しておきます。
背景
- React Hooksの
useEffect()
第2引数に渡す配列は、「この副作用が依存する値」を表す。- つまり、指定した配列に含まれる値が変化したときのみ、第1引数の関数が実行されるようにできる
- では、変化の検知はどういう方式なのか?
- 具体的には、配列やオブジェクトを渡した場合にdeepな比較をしてくれるのか?
結論
- Deepな比較はしてくれない。
- 値が全く同じでも、別オブジェクトなら変更検知される。逆もまた然り。
- とはいえ、通常は
props
,state
くらいしか指定しないだろうから、同じオブジェクトだけど値が変わった、みたいな状況はほぼ気にしなくて良さそうではある
関連記事
- ほぼ同じ内容の記事があることに気づいたので、引用させていただきます
実験
useEffect()
の第2引数に
- 数値をプロパティに持つオブジェクト
- 数値を要素に持つ配列
- プリミティブ数値
を渡し、各数値をインクリメントしつつ1秒おきにrenderする。
直接innerText
を書き換えるような副作用を仕掛けておき、差分が検出されるか調べた。
実際、オブジェクト・配列については中身の値が変わっていても副作用が実行されなかった。
コード確認
- React v16.13.1 を読んだ
- Reactのコードをちゃんと追うのは時間がかかりそうなので断念した
- 当たりを付けた上で、DevToolsでデバッグして実際に該当コードが実行されていることを確認した
useEffect()
の実装
- React内部では、
ReactCurrentDispatcher.current
というグローバル変数を状況に応じて差し替えながら処理しているらしい- 例えばReactFiberHooks.js:402
- 更新描画時には、
HooksDispatcherOnUpdate
が使われる
- それを元に追いかけると、
useEffect()
の実体がupdateEffectImpl()
という関数であることがわかる - 差分検知は
areHookInputsEqual()
がしている