React には useEffect、useMemo、useCallbackといった特定の値が変わった時、それを検知して動くフックが用意されています
副作用フックの利用法 – React
フック API リファレンス – React#useCallback
フック API リファレンス – React#useMemo
これらは次の様にある値が変わったらその値を使ってなんやかんやする、という目的で使うフックです
// a か b の値が変わったら、関数の部分を最新の a と b で実行して、結果を保持する const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
こういったフックは便利なのですが、まれに依存関係の見逃しから期待しない挙動をする時があります。これは例えば次です。
// b が変わったときも値を更新して欲しいのに a が変わった時しか更新しない const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a]);
この問題は第一引数の関数が依存している関数外の値が全て第二引数に含まれていれば概ね解決します(たまにあえて更新しない方がいい時もあります)。ESLint のプラグインである eslint-plugin-react-hooks にはこれを検知するためのルールが備わっています。
使い方は他の一般的な ESLint のプラグインと同様です。
# eslint とプラグインを追加 npm i --save-dev eslint eslint-plugin-react-hooks
// .eslintrc.js // ESLint のルールを定義 module.exports = { parserOptions: { ecmaVersion: 2022, sourceType: 'module', }, // プラグインを有効化 plugins: ['react-hooks'], rules: { // 正常に動かなさそうなフックを検知するルールを追加 'react-hooks/exhaustive-deps': 'warn', }, };
この状態でnode_modules\.bin\eslint tmp.js
の様に実行すると次の様になります。
import { useMemo } from 'react'; export const useWrapperHook = (a, b) => { const memoizedValue = useMemo(() => a + b, [a]); // 4:46 warning React Hook useMemo has a missing dependency: 'b'. Either include it or remove the dependency array react-hooks/exhaustive-deps // // ✖ 1 problem (0 errors, 1 warning) return { memoizedValue }; };
内部の関数が依存している変数 b が依存を表す配列に含まれていないという警告が出力されました。
例はシンプルなコードですが、実際に何かを作ると複雑で巨大になりがちです。なにがしかで問題を早期発見できるようにしておくと何かと便利です。