O hirunewani blog

なぜreact-hooks/exhaustive-depsはただcomponentDidMountのように使いたいだけなのに依存関係を入れてくるのか

Created at

useEffectのdepsに依存関係を入れる理由について解説する。

export const Example = ({ loadOnlyOnce }) => {
  useEffect(() => {
    loadOnlyOnce();
  }, [loadOnlyOnce]); // ← WHY?
};

componentDidMount/componentDidUpdateは本来一つのフロー

クラスコンポーネント時代のReactにおけるcomponentDidMountなどのライフサイクルメソッドの最大の問題点は、それを独立したメソッドと捉えさせてしまうこと。実際にはフローの一部であり、 componentDidMountの中で何かを参照する場合、本来同様にcomponentDidUpdateの中でそれを処理する必要がありる。そうしなければコンポーネントはバグを持つ可能性がある。 これはreact-hooks/exhaustive-depsが修正しようとしているものであり、時間をかけて値を処理するように促している。

次のようなフローを考える。

  1. Componentがマウントしたら、propと何かをする。
    • ロジックを componentDidMount または useEffect のbodyに配置する。
  2. Componentが更新されたら (propの値(参照)が変更されたら), 新しいpropと何かをする。
    • ロジックをcomponentDidUpdateまたはuseEffectのdepsに配置する。

react-hooks/exhaustive-depsはこのフローの2をやっていないと注意している。

実際のところ、useEffect自体がこのフローと発生しうるバグに対して立ち向かうことを強いるように設計されている。

自分が思っているだけでは相手に伝わらない

その処理が確実に一度しか行われないと分かる名前でもない限り、それが本当に一度しか行われないのか、依存関係が本当に不変なのか、他の人がすぐに理解することは出来ません。そして、このルールやReact、javascriptは他人よりもっと分からずやです。

空の配列を提供することは、「自分のしていることはわかっている」と言っているのと変わらない。

そして何より実際に依存関係が決して変わらないのであれば、含めても問題はない。