💣

取り扱い注意!useCallback依存配列に入れちゃいけないもの

2022/12/07に公開

hooks依存配列に含めると都合の悪いものがあるらしいので記事に起こすことにしました。

本記事はCatallaxyqiitaアドベントカレンダー12/21のものになります。

useCallback

is何

メモ化されたコールバックを返します。

インラインのコールバックとそれが依存している値の配列を渡してください。useCallback はそのコールバックをメモ化したものを返し、その関数は依存配列の要素のいずれかが変化した場合にのみ変化します。これは、不必要なレンダーを避けるために(例えば shouldComponentUpdate などを使って)参照の同一性を見るよう最適化されたコンポーネントにコールバックを渡す場合に便利です。

useCallback(fn, deps) は useMemo(() => fn, deps) と等価です。

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

https://ja.reactjs.org/docs/hooks-reference.html#usecallback

実際にダメなケース

以下のようなカスタムフックを実装するとします。

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を扱うようにするのがベストかと。

以上、自分の学びを共有できれば幸甚です。

catallaxy tech blog

Discussion