验证组件和钩子是否遵循 钩子的规则

¥Validates that components and hooks follow the Rules of Hooks.

规则详情

¥Rule Details

React 依赖于钩子的调用顺序来正确保存渲染之间的状态。每次组件渲染时,React 都期望以完全相同的顺序调用相同的钩子。当有条件地或在循环中调用钩子时,React 会丢失哪个状态对应于哪个钩子调用的跟踪,从而导致状态不匹配和 “渲染的钩子数量少于/多于预期” 错误等错误。

¥React relies on the order in which hooks are called to correctly preserve state between renders. Each time your component renders, React expects the exact same hooks to be called in the exact same order. When hooks are called conditionally or in loops, React loses track of which state corresponds to which hook call, leading to bugs like state mismatches and “Rendered fewer/more hooks than expected” errors.

常见违规

¥Common Violations

这些模式违反了钩子规则:

¥These patterns violate the Rules of Hooks:

  • 条件中的钩子(if/else、三元组、&&/||

    ¥Hooks in conditions (if/else, ternary, &&/||)

  • 循环中的钩子(forwhiledo-while

    ¥Hooks in loops (for, while, do-while)

  • 提前返回后的钩子

    ¥Hooks after early returns

  • 回调/事件处理程序中的钩子

    ¥Hooks in callbacks/event handlers

  • 异步函数中的钩子

    ¥Hooks in async functions

  • 类方法中的钩子

    ¥Hooks in class methods

  • 模块级钩子

    ¥Hooks at module level

注意

use 钩子

¥use hook

use 钩子与其他 React 钩子不同。你可以有条件地或循环地调用它:

¥The use hook is different from other React hooks. You can call it conditionally and in loops:

// ✅ `use` can be conditional
if (shouldFetch) {
const data = use(fetchPromise);
}

// ✅ `use` can be in loops
for (const promise of promises) {
results.push(use(promise));
}

但是,use 仍然有限制:

¥However, use still has restrictions:

  • 不能封装在 try/catch 中

    ¥Can’t be wrapped in try/catch

  • 必须在组件或钩子内部调用

    ¥Must be called inside a component or hook

了解更多:use API 参考

¥Learn more: use API Reference

无效

¥Invalid

此规则的错误代码示例:

¥Examples of incorrect code for this rule:

// ❌ 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

有效

¥Valid

此规则的正确代码示例:

¥Examples of correct code for this rule:

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>;
}

故障排除

¥Troubleshooting

我想要根据某些条件获取数据

¥I want to fetch data based on some condition

你正在尝试有条件地调用 useEffect:

¥You’re trying to conditionally call useEffect:

// ❌ Conditional hook
if (isLoggedIn) {
useEffect(() => {
fetchUserData();
}, []);
}

无条件调用钩子,并检查内部条件:

¥Call the hook unconditionally, check condition inside:

// ✅ Condition inside hook
useEffect(() => {
if (isLoggedIn) {
fetchUserData();
}
}, [isLoggedIn]);

注意

除了 useEffect 之外,还有更好的方法来获取数据。考虑使用 React Query、useSWR 或 React Router 6.4+ 进行数据获取。这些解决方案可以处理重复请求、缓存响应并避免网络瀑布。

¥There are better ways to fetch data rather than in a useEffect. Consider using React Query, useSWR, or React Router 6.4+ for data fetching. These solutions handle deduplicating requests, caching responses, and avoiding network waterfalls.

了解更多:获取数据

¥Learn more: Fetching Data

我需要针对不同场景使用不同的状态

¥I need different state for different scenarios

你正在尝试有条件地初始化状态:

¥You’re trying to conditionally initialize state:

// ❌ Conditional state
if (userType === 'admin') {
const [permissions, setPermissions] = useState(adminPerms);
} else {
const [permissions, setPermissions] = useState(userPerms);
}

始终调用 useState,并有条件地设置初始值:

¥Always call useState, conditionally set the initial value:

// ✅ Conditional initial value
const [permissions, setPermissions] = useState(
userType === 'admin' ? adminPerms : userPerms
);

选项

¥Options

你可以使用共享的 ESLint 设置(在 eslint-plugin-react-hooks 6.1.1 及更高版本中可用)配置自定义效果钩子:

¥You can configure custom effect hooks using shared ESLint settings (available in eslint-plugin-react-hooks 6.1.1 and later):

{
"settings": {
"react-hooks": {
"additionalEffectHooks": "(useMyEffect|useCustomEffect)"
}
}
}
  • additionalEffectHooks:用于匹配自定义钩子的正则表达式模式,这些钩子应该被视为效果。这允许从自定义效果钩子中调用 useEffectEvent 和类似的事件函数。

    ¥additionalEffectHooks: Regex pattern matching custom hooks that should be treated as effects. This allows useEffectEvent and similar event functions to be called from your custom effect hooks.

此共享配置由 rules-of-hooksexhaustive-deps 规则使用,以确保所有与钩子相关的 linting 行为一致。

¥This shared configuration is used by both rules-of-hooks and exhaustive-deps rules, ensuring consistent behavior across all hook-related linting.