💨
(初学者が学んだ)React 依存配列について
はじめに
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を使う理由といったところを深ぼってよりよいコードを選択していければと思います。
We're Hiring!
DELTAではチームの一員になっていただける仲間を募集中です!
下記フォームよりお気軽にご連絡ください!
Discussion
失礼します。
依存配列ですが、これを手動・目視で管理するのは困難であり、そもそも React の設計意図として、そうすべきではありません。どうすべきかというと、コールバックの中で使っている Prop / State 等を自動的に列挙するべきです。
自動的に列挙する方法として、現在は ESLint の react-hooks/exhaustive-deps ルールを使います。もう一つの方法として、現在開発中ですが、React Compiler を利用するのも手です。(React Compiler はそもそもメモ化を自動化して、useMemo, useCallback を不要にする)
あと、これは邪推ですが、「Sample1 呼び出して、その次には Sample 2 を呼び出す」のは、以下のような罠に引っかかって、思ったのと違う挙動になる気がします。
つまづいた場合はぜひこちらを参照してください。