取り扱い注意!useCallback依存配列に入れちゃいけないもの
hooks依存配列に含めると都合の悪いものがあるらしいので記事に起こすことにしました。
本記事はCatallaxyのqiitaアドベントカレンダー12/21のものになります。
useCallback
is何
メモ化されたコールバックを返します。
インラインのコールバックとそれが依存している値の配列を渡してください。useCallback はそのコールバックをメモ化したものを返し、その関数は依存配列の要素のいずれかが変化した場合にのみ変化します。これは、不必要なレンダーを避けるために(例えば shouldComponentUpdate などを使って)参照の同一性を見るよう最適化されたコンポーネントにコールバックを渡す場合に便利です。
useCallback(fn, deps) は useMemo(() => fn, deps) と等価です。
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
実際にダメなケース
以下のようなカスタムフックを実装するとします。
interface Props{
onHoge : () => void;
isHoge: boolean;
}
export const useHoge = (props:Props) => {
・・・
const fuga = useCallback(()=>{
props.onHoge();
・・・
},[props.onHoge,・・・]
)
}
このようにpropsとして渡された関数onHoge
をuseCallback内で実行し、そのまま依存配列に含めてしまうと以下のようにeslintに怒られてしまうようです。
eslint error
React Hook useCallback has a missing dependency: 'props'. Either include it or remove the dependency array. However, 'props' will change when any prop changes, so the preferred fix is to destructure the 'props' object outside of the useCallback call and refer to those specific props inside useCallback.
これに素直に従い、
const fuga = useCallback(()=>{
props.onHoge();
・・・
},[props,・・・]
)
とすることでエラー自体は解消されます。
つまり、props.onHoge
のみならず、props.isHoge
も依存配列に含めないといけなくなることになります。これだとprops.isHoge
にも依存するようになり、期待する挙動になりえない可能性が出てきてしまいます。
解決策
- 依存配列に
props.onHoge
のみを入れたい - 他のpropsは依存させたくない
これらを実現するためには分割代入するのが最も手っ取り早いと考えました。
interface Props{
onHoge : () => void;
isHoge: boolean;
}
export const useHoge = (props:Props) => {
const { onHoge } = props;
・・・
const fuga = useCallback(()=>{
onHoge();
・・・
},[onHoge,・・・]
)
}
このように先にコールバック関数fuga
を定義する前に分割代入してしまえば、依存関係を壊さずに済みそうです。
useMemoについても同様
const fuga = useMemo(() => {
return props.onHoge();
}, [props.onHoge]);
としてもやはり同様のエラーが出るようです。なので同じく分割代入してからonHoge
を扱うようにするのがベストかと。
以上、自分の学びを共有できれば幸甚です。
Discussion