rules-of-hooks
コンポーネントとフックがフックのルールに従っているか検証します。
ルールの詳細
React はレンダー間で state を正しく保持するために、フックが呼び出される順番を用います。コンポーネントがレンダーされるたびに、まったく同じフックがまったく同じ順序で呼び出されることを期待しています。フックを条件付きで呼び出したり、ループ内で呼び出したりすると、どの state がどのフック呼び出しに対応するのか React が分からなくなり、state の不一致や “Rendered fewer/more hooks than expected” といったエラーにつながります。
よくある違反
以下のパターンは、フックのルールに違反します。
- 条件分岐内でのフックの呼び出し(
if/else、三項演算子、&&/||) - ループ内でのフックの呼び出し (
for,while,do-while) - 早期リターンの後でのフックの呼び出し
- コールバックやイベントハンドラ内でのフックの呼び出し
- 非同期関数内でのフックの呼び出し
- クラスメソッド内でのフックの呼び出し
- モジュールレベルでのフックの呼び出し
無効な例
このルールに違反するコードの例です。
// ❌ Hook in condition
if (isLoggedIn) {
const [user, setUser] = useState(null);
}
// ❌ Hook after early return
if (!data) return <Loading />;
const [processed, setProcessed] = useState(data);
// ❌ Hook in callback
<button onClick={() => {
const [clicked, setClicked] = useState(false);
}}/>
// ❌ `use` in try/catch
try {
const data = use(promise);
} catch (e) {
// error handling
}
// ❌ Hook at module level
const globalState = useState(0); // Outside component有効な例
このルールに従ったコードの例です。
function Component({ isSpecial, shouldFetch, fetchPromise }) {
// ✅ Hooks at top level
const [count, setCount] = useState(0);
const [name, setName] = useState('');
if (!isSpecial) {
return null;
}
if (shouldFetch) {
// ✅ `use` can be conditional
const data = use(fetchPromise);
return <div>{data}</div>;
}
return <div>{name}: {count}</div>;
}トラブルシューティング
条件に応じてデータをフェッチしたい
以下では、useEffect を条件付きで呼び出そうとしています。
// ❌ Conditional hook
if (isLoggedIn) {
useEffect(() => {
fetchUserData();
}, []);
}フックは無条件で呼び出すことにし、条件はその内部で確認してください。
// ✅ Condition inside hook
useEffect(() => {
if (isLoggedIn) {
fetchUserData();
}
}, [isLoggedIn]);状況に応じて別の state が欲しい
以下では、state を条件付きで初期化しようとしています。
// ❌ Conditional state
if (userType === 'admin') {
const [permissions, setPermissions] = useState(adminPerms);
} else {
const [permissions, setPermissions] = useState(userPerms);
}useState は常に呼び出すようにし、初期値を条件に応じて設定してください。
// ✅ Conditional initial value
const [permissions, setPermissions] = useState(
userType === 'admin' ? adminPerms : userPerms
);オプション
ESLint の共有設定を使って、カスタムエフェクトフックを設定できます(eslint-plugin-react-hooks 6.1.1 以降で利用可能)。
{
"settings": {
"react-hooks": {
"additionalEffectHooks": "(useMyEffect|useCustomEffect)"
}
}
}additionalEffectHooks: エフェクトとして扱うカスタムフックにマッチする正規表現パターン。これにより、カスタムエフェクトフックからuseEffectEventや同様のイベント関数を呼び出せるようになります。
この共有設定は、rules-of-hooks と exhaustive-deps の両方のルールで使われ、フック関連のすべてのリントで一貫した動作が保証されます。