本文ではreact hooksであるuseMemo, useCallbackについて整理する。
useMomo
説明
useMemoは複雑なロジックから出された結果を保存する。
依存配列には再計算するための状態が入る。
const testCalculate = (a, b) => a + b;
const testMemo = useMemo(() => {
return testCalculate(a,b);
}, [a, b]);
上記の例で言うなら、testCalculateを直接使用した場合は毎回testCalculate関数を走らせる。
しかし、testMemoを使用するのであれば、a又はbが変更されない限り、testCalculateが走ることはなく、事前に計算したa+bの結果の値をそのまま利用する。(再計算されない。)
いつ使うか
これは複雑な計算ロジックに使われることが多い。上記の例のように簡単なロジックの場合は、関数を直接使用した方が効率的になる。
よって、useMemoを使う場合は、「関数を再実行するコスト(CPU/メモリ) > 結果を保存しておくメモリコスト」だと考えよう。
useCallback
説明
useCallbackは関数そのものを保存する。
依存配列には関数を新しく作るトリガーになる状態が入る。
const testCalculate = (a, b) => a + b;
const testCallback = useCallback(() => {
return testCalculate(a,b);
}, [a, b]);
上記を例にするのであればtestCalculateを直接使用する場合は毎回testCalculateという関数を作ることになる。しかし、testCallbackを利用すればtestCalculate関数はa又はbが変更されない限り、保存していた関数の参照(メモリ住所)を再利用する」。
ここで重要になるのが関数を実行することと作ることの違いを明確に分ける必要がある。
jsで関数を作ることはtestCalculateの中のロジックは同じで合っても別のメモリを割り当てることになる。しかし、testCallbackを使う場合は事前にtestCalculateが保存されているメモリを直接参照することで関数を新しく作成(新しいメモリに割り当てることない)する。
useCallbackを利用するために理解するべきもう一つの概念はレンダリングである。reactはAppコンポーネントの中の全てを毎回レンダリングしている。これにより、関数をAppコンポーネントの中に書いてる場合は、useCallbackで関数を囲んでも関数を「作る」ことになる。よって、useCallbackを利用する場合、関数はappの外に定義しておくべきだろう。
疑問点
上記でuseMemo, useCallbackについて説明したが、結局useMemo, useCallbackって結果同じことになるのではと思う開発者もいるだろう。
reactの内部を見てみよう。
function useCallback(fn, deps) {
return useMemo(() => fn, deps);
}
結果から言うと「同じ」である。
ただし、可読性の面から考えた場合、useMemoは値を保存しておきたい場合に使い、useCallbackは関数を保存しておきたい場合に使おうとの約束になる。