はじめに
この記事では、ESLint の Flat Config に React Hooks 向けルールのプラグインである eslint-plugin-react-hooks
を追加する手順について記載します。
開発環境
開発環境は以下の通りです。
- Windows 11
- VSCode
- TypeScript 5.2.2
- React 18.3.1
- Vite 5.3.1
- Node.js 20.15.0
- npm 10.8.1
- ESLint 8.57.0
- Prettier 3.3.2
- @eslint/js 9.6.0
- eslint-config-prettier 9.1.0
- eslint-plugin-jsx-a11y 6.9.0
- eslint-plugin-react 7.34.3
- eslint-plugin-react-hooks 4.6.2
- globals 15.8.0
- typescript-eslint 7.15.0
事前準備
プロジェクトに ESLint のインストール・設定をしておきます。
React / TypeScript 環境における詳細手順はこちら。
Prettier を追加する手順はこちら。
eslint-plugin-react-hooks
のインストール
eslint-plugin-react-hooks
をインストールします。
npm install eslint-plugin-react-hooks --save-dev
一括設定
今までの記事で扱った以下のプラグインと異なり、2024年7月時点で、eslint-plugin-react-hooks
には Flat Config 向けの共有設定(Shareable Configs
)が提供されていないようです。
そのため、上記のプラグインとは、設定方法が異なります。
-
eslint-plugin-react-hooks
をインポート -
pugins
に"react-hooks": pluginReactHooks
を追加 -
rules
に...pluginReactHooks.configs.recommended.rules
を追加
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginReactConfig from "eslint-plugin-react/configs/recommended.js";
import pluginReactJSXRuntime from "eslint-plugin-react/configs/jsx-runtime.js";
import pluginReactHooks from "eslint-plugin-react-hooks"; // Add
import jsxA11y from "eslint-plugin-jsx-a11y";
import eslintConfigPrettier from "eslint-config-prettier";
export default [
{
files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
settings: {
react: {
version: "detect",
},
},
plugins: {
"react-hooks": pluginReactHooks, // Add
},
rules: {
...pluginReactHooks.configs.recommended.rules, // Add
},
},
{ languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
pluginReactConfig,
pluginReactJSXRuntime,
jsxA11y.flatConfigs.recommended,
eslintConfigPrettier,
];
動作確認
useEffect
の dependencies
がない状態で、ESLint を実行してみます。
import { useEffect, useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
alert("Count: " + count);
}, []); // no dependencies
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank" rel="noreferrer">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
);
}
export default App;
以下のワーニングが表示されます。
11:6 warning React Hook useEffect has a missing dependency:'count'.
Either include it or remove the dependency array
react-hooks/exhaustive-deps
✖ 1 problem (0 errors, 1 warning)
count
を追加するとワーニングが消えます。
import { useEffect, useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
function App() {
const [count, setCount] = useState(0);
useEffect(() => {
alert("Count: " + count);
}, [count]); // Add
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank" rel="noreferrer">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank" rel="noreferrer">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
);
}
export default App;
個別設定
eslint-plugin-react-hooks
の各ルールを個別に設定することもできます。
rules
へ ...pluginReactHooks.configs.recommended.rules
の代わりに個別のルールを記載することで設定できます。
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginReactConfig from "eslint-plugin-react/configs/recommended.js";
import pluginReactJSXRuntime from "eslint-plugin-react/configs/jsx-runtime.js";
import pluginReactHooks from "eslint-plugin-react-hooks";
import jsxA11y from "eslint-plugin-jsx-a11y";
import eslintConfigPrettier from "eslint-config-prettier";
export default [
{
files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"],
settings: {
react: {
version: "detect",
},
},
plugins: {
"react-hooks": pluginReactHooks,
},
rules: {
"react-hooks/exhaustive-deps": "error", // Add
},
},
{ languageOptions: { parserOptions: { ecmaFeatures: { jsx: true } } } },
{ languageOptions: { globals: globals.browser } },
pluginJs.configs.recommended,
...tseslint.configs.recommended,
pluginReactConfig,
pluginReactJSXRuntime,
jsxA11y.flatConfigs.recommended,
eslintConfigPrettier,
];
参考
関連記事