depsに入れなくて良い、ではなく、入れてはならない です。
エフェクトイベント関数をdepsに入れるとレンダリング毎にエフェクトが走る
useEffectEventは レンダリングごとに新しい関数を返します (検証用デモ)。 そのため、useEffect の deps にエフェクトイベントを含めると毎回エフェクトが実行されます(時にはこれによって 無限ループが起きます )
const effectEventFn = useEffectEvent(callback); // レンダリングのたびに新しい関数が返される useEffect(() => { return listen(effectEventFn); }, [effectEventFn]); // ❌ effectEventFn が入っているので毎回実行される!!
なので、depsにeffectEventFnを入れてはいけません。
useEffect(() => { return listen(effectEventFn); }, []); // ⭕️ depsにエフェクトイベント関数が入ってない
なぜこのような実装になったのでしょうか?
背景: ランタイムでのアサーションとして機能させるため
この意思決定の背景はこのPRが参考になります。
[useEvent] Non-stable function identity by poteto · Pull Request #25473 · facebook/react · GitHub
これによると、「useEffectEventがstableであることに依存したエフェクトを再実行することで、depsにエフェクトイベントを入れてはいけないとユーザーに学んでもらうため 」にunstableに変更したようです。
補足説明: なぜunstableであるべきなのか?
上の説明だけではわかりづらいので、いくらか補足します。
まず、useEffectEventを stableで定義する (初回に作成した関数が常に返される) と、useEffectのdepsに入れても入れなくても同じ挙動になります。 実際、stableで定義されているpolyfillの方は、depsに入れても動きます。
useEffect(() => { return listen(effectEventFn); }, [effectEventFn]); // effectEventFnがstableだったらこれでも問題ない
これに対し、useEffectEventを unstableで定義する (レンダリング毎に新しい関数が返される) と、以下のようになります。
- useEffectのdepsに入れると毎回エフェクトが走る
- depsに入れないと更新されてもスキップされる
reactのメンタルモデル的には、useEffectのdepsに入れないで欲しいようです。 そのため、"ランタイムアサーション"が追加されている方が良いという結論になったようです。
まとめ: useEffectのdepsは自分で「選ぶ」たぐいの物ではない
今までのuseEffectのdepsは以下の2種類でした。
- depsに入れても影響しないもの(refなど)
- depsに入れるとエフェクトがスキップされるようになるもの
19.2でエフェクトイベント関数が加わったことで、さらに以下が加わりました。
- depsに入れると不要なエフェクトが走るようになるもの
以前からドキュメントにて useEffectのdepsは 自分で「選ぶ」たぐいの物ではない
*1と明言されています。エフェクトイベントもこれに照らし合せた実装が行われたということです。 エフェクトイベントの追加がされた今、改めてこのことを肝に銘じておきましょう。