💨

(初学者が学んだ)React 依存配列について

に公開2

はじめに

React初心者が躓いたところを、学習した内容を自分で見返すためにも記事にします。

まずは起こった事象

ざっくりこんなコードがあり
Sample1が実行された後、Sample2を実行すると”hello”が出力される想定だったのですが、出力されませんでした。

  const [Id, setId] = useState<string | null>(null);
  
  const Sample1 = useCallback(async (Id: string) => {
    setId(Id);
  }, []);

  const Sample2 = useCallback(async () => {
    if (!Id) return;
    console.log("hello")
  }, []);

原因

原因はSample2の依存配列が空だったことでした。
正しくは以下で、そうすると想定通りの挙動をとるようになりました。

  const [Id, setId] = useState<string | null>(null);
  
  const Sample1 = useCallback(async (Id: string) => {
    setId(Id);
  }, []);

  const Sample2 = useCallback(async () => {
    if (!Id) return;
    console.log("hello")
  }, [Id]);

うまくいかなかった原因としては、依存配列が空の状態だと、
Sample2 関数は コンポーネント初回マウント時に一度だけ生成され、その後は変わらない。

つまり、Sample1でIdがセットされても、Sample2は引き続き初回で生成された関数が使用されるので、Idに値がないということでなにも実行されずにreturnされていました。

おわりに

今回は依存配列についてちゃんと理解していなかったために発生した事象で、
実際にうまくいかない挙動から原因を調査することで理解が深まりました。

(おまけ)そもそもuseCallbackを使う必要があるのか?

今回、特に最善なコードを考えずにuseCallbackを使用していましたが、
そもそも普通の関数にしていれば、useStateの変化によってコンポーネント全体が再レンダーされ今回のような問題は起きなかったはずです。

useCallbackを使う理由としてはこのような記事もありましたので、
次回はuseCallbackを使う理由といったところを深ぼってよりよいコードを選択していければと思います。
https://tech.asoview.co.jp/entry/2020/12/06/000000

We're Hiring!

DELTAではチームの一員になっていただける仲間を募集中です!
下記フォームよりお気軽にご連絡ください!

https://docs.google.com/forms/d/e/1FAIpQLSfQuWNU1il5lq2rVdICM0tSK_jTsjqwc52LYEwUxBq7_ImtrQ/viewform

DELTAテックブログ

Discussion

Honey32Honey32

失礼します。

依存配列ですが、これを手動・目視で管理するのは困難であり、そもそも React の設計意図として、そうすべきではありません。どうすべきかというと、コールバックの中で使っている Prop / State 等を自動的に列挙するべきです。

自動的に列挙する方法として、現在は ESLint の react-hooks/exhaustive-deps ルールを使います。もう一つの方法として、現在開発中ですが、React Compiler を利用するのも手です。(React Compiler はそもそもメモ化を自動化して、useMemo, useCallback を不要にする)

dependencies: fn コード内で参照されるすべてのリアクティブな値のリストです。リアクティブな値には、props、state、コンポーネント本体に直接宣言されたすべての変数および関数が含まれます。リンタが React 用に設定されている 場合、すべてのリアクティブな値が依存値として正しく指定されているか確認できます。依存値のリストは要素数が一定である必要があり、[dep1, dep2, dep3] のようにインラインで記述する必要があります。React は、Object.is を使った比較で、それぞれの依存値を以前の値と比較します。

https://ja.react.dev/reference/react/useCallback#parameters