useCallbackのユースケース3選
自分がReactに入門した頃は、メモ化に対して漠然と難しそうなイメージを持っていました。
今思い返すと「メモ化を使うとキャッシュできるのはわかるけど、具体的にどういうときに使うのかわからない」ような感情をいだいていた記憶があります。
ということで今回はメモ化の中でも関数をメモするuseCallback
を対象に、useCallback
のユースケースは3つしかないという話をします。
-
memo
化されたコンポーネントのpropsにわたす関数 -
useEffect
やuseMemo
の依存に含まれる関数 - カスタムフック内の関数
ユースケースが3つしかないとわかれば、少なくとも「漠然と難しそう」という状態を脱することができるはずです。
1. memo化されたコンポーネントのpropsにわたす関数
コンポーネントをmemo
化すると、propsが変更しないときに再レンダリングをスキップすることができます。 しかし、以下の例ではmemo
が全く意味のないものになります。
const Child = memo(({ onClick }) => {});
const Parent = () => {
const handleClick = () => {};
return <Child onClick={handleClick} />
};
どういうことかというと、関数はレンダリングごとに異なる参照を生成するので、レンダリングごとにhandleClick
つまりonClick
propsが変化したと見なされます。
propsが変化したときはmemo
の有無に関わらず子コンポーネントが再レンダリングされるので、memo
が意味のないものになっているということですね。
これを解消するためにはhandleClick
をuseCallback
でラップします。
const Child = memo(({ onClick }) => {});
const Parent = () => {
const handleClick = useCallback(() => {}, []);
return <Child onClick={handleClick} />
};
ちなみに子コンポーネントのmemo
を削除すると、今度はuseCallback
が意味のないものになります。
const Child = ({ onClick }) => {};
const Parent = () => {
const handleClick = useCallback(() => {}, []);
return <Child onClick={handleClick} />
};
各レンダリング間でhandleClick
は同一のものと見なされますが、そもそもmemo
化されていないコンポーネントは毎回再レンダリングされるので、useCallback
を使うだけでは子コンポーネントの再レンダリングを抑制することはできないということです。
2. useEffectやuseMemoの依存に含まれる関数
先ほどと同じ理由ですが、useEffect
やuseMemo
の依存に関数を渡すだけだと、それらは毎回実行されてしまいます。
そのため、依存に渡す関数はuseCallback
でラップする必要があります。
const Component = () => {
const doSomething = useCallback(() => {}, []);
useEffect(() => {
doSomething();
}, [doSomething]);
// ...
};
またuseCallback
は、関数をuseEffect
やuseMemo
の中かコンポーネント外に移動させることで、不要なuseCallback
や依存を減らすことができる可能性があります。
const Component = () => {
useEffect(() => {
const doSomething = () => {};
doSomething();
}, []);
// ...
};
もしくは
const doSomething = () => {};
const Component = () => {
useEffect(() => {
doSomething();
}, []);
// ...
};
コードがよりシンプルになりましたね。
3. カスタムフック内の関数
自分はカスタムフックから返される関数はすべてuseCallback
でラップするようにしています。
カスタムフックを作る理由は、普通の関数を作る理由と全く同じであり、すなわち責務の分離とかカプセル化です。 一度カスタムフックとして分離された以上、インターフェースの内側のことはカスタムフック内で完結すべきです。 カスタムフックを使う側はカスタムフックの内側のことを知るべきではなく、その逆も然りです。
詳しくは以下の記事を参照することをおすすめします。
カスタムフックから返された関数も結局のところ、ケース1(memo
化されたコンポーネントのpropsにわたす)または、ケース2(useEffect
やuseMemo
の依存にわたす)として使われるのですが、カスタムフックが絡むと考え方が変わるのでケース3として紹介しました。
最後に
今回は関数をメモ化するuseCallback
のユースケースについて紹介しました。
useCallback
と似たものにuseMemo
がありますが、useMemo
はuseCallback
とは異なり、オブジェクト(関数含む)だけではなくプリミティブな値もメモ化しようと思えばできてしまいます。
つまりメモ化する基準や考え方が異なるので、別途学習すると良いでしょう。最後にuseMemo
について、個人的にわかりやすいなと思った参考記事を添付しておきます。
Discussion