验证 React 钩子的依赖数组是否包含所有必要的依赖。
¥Validates that dependency arrays for React hooks contain all necessary dependencies.
规则详情
¥Rule Details
像 useEffect
、useMemo
和 useCallback
这样的 React 钩子接受依赖数组。当这些钩子函数中引用的值未包含在依赖数组中时,React 将不会重新运行该副作用或在该依赖发生变化时重新计算该值。这会导致钩子使用过时的值,从而产生过时的闭包。
¥React hooks like useEffect
, useMemo
, and useCallback
accept dependency arrays. When a value referenced inside these hooks isn’t included in the dependency array, React won’t re-run the effect or recalculate the value when that dependency changes. This causes stale closures where the hook uses outdated values.
常见违规
¥Common Violations
当你尝试使用 “trick” React 来控制依赖以控制副作用的运行时间时,通常会发生此错误。Effect 应该将组件与外部系统同步。依赖数组会告诉 React 副作用使用哪些值,因此 React 知道何时重新同步。
¥This error often happens when you try to “trick” React about dependencies to control when an effect runs. Effects should synchronize your component with external systems. The dependency array tells React which values the effect uses, so React knows when to re-synchronize.
如果你发现自己与 linter 发生冲突,则可能需要重构代码。请参阅 移除副作用依赖 了解如何操作。
¥If you find yourself fighting with the linter, you likely need to restructure your code. See Removing Effect Dependencies to learn how.
无效
¥Invalid
此规则的错误代码示例:
¥Examples of incorrect code for this rule:
// ❌ Missing dependency
useEffect(() => {
console.log(count);
}, []); // Missing 'count'
// ❌ Missing prop
useEffect(() => {
fetchUser(userId);
}, []); // Missing 'userId'
// ❌ Incomplete dependencies
useMemo(() => {
return items.sort(sortOrder);
}, [items]); // Missing 'sortOrder'
有效
¥Valid
此规则的正确代码示例:
¥Examples of correct code for this rule:
// ✅ All dependencies included
useEffect(() => {
console.log(count);
}, [count]);
// ✅ All dependencies included
useEffect(() => {
fetchUser(userId);
}, [userId]);
故障排除
¥Troubleshooting
添加函数依赖会导致无限循环
¥Adding a function dependency causes infinite loops
你有一个 effect,但你在每次渲染时都会创建一个新函数:
¥You have an effect, but you’re creating a new function on every render:
// ❌ Causes infinite loop
const logItems = () => {
console.log(items);
};
useEffect(() => {
logItems();
}, [logItems]); // Infinite loop!
在大多数情况下,你不需要 effect。改为调用执行操作的函数:
¥In most cases, you don’t need the effect. Call the function where the action happens instead:
// ✅ Call it from the event handler
const logItems = () => {
console.log(items);
};
return <button onClick={logItems}>Log</button>;
// ✅ Or derive during render if there's no side effect
items.forEach(item => {
console.log(item);
});
如果你确实需要该效果(例如,订阅外部内容),请确保依赖稳定:
¥If you genuinely need the effect (for example, to subscribe to something external), make the dependency stable:
// ✅ useCallback keeps the function reference stable
const logItems = useCallback(() => {
console.log(items);
}, [items]);
useEffect(() => {
logItems();
}, [logItems]);
// ✅ Or move the logic straight into the effect
useEffect(() => {
console.log(items);
}, [items]);
仅运行副作用一次
¥Running an effect only once
你想在挂载时运行一次 effect,但 linter 提示缺少依赖:
¥You want to run an effect once on mount, but the linter complains about missing dependencies:
// ❌ Missing dependency
useEffect(() => {
sendAnalytics(userId);
}, []); // Missing 'userId'
如果确实需要运行一次,请包含依赖(推荐)或使用 ref:
¥Either include the dependency (recommended) or use a ref if you truly need to run once:
// ✅ Include dependency
useEffect(() => {
sendAnalytics(userId);
}, [userId]);
// ✅ Or use a ref guard inside an effect
const sent = useRef(false);
useEffect(() => {
if (sent.current) {
return;
}
sent.current = true;
sendAnalytics(userId);
}, [userId]);
选项
¥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
:用于匹配自定义钩子的正则表达式模式,这些钩子应该被检查是否存在详尽依赖。此配置在所有react-hooks
规则中共享。¥
additionalEffectHooks
: Regex pattern matching custom hooks that should be checked for exhaustive dependencies. This configuration is shared across allreact-hooks
rules.
为了向后兼容,此规则还接受一个规则级选项:
¥For backward compatibility, this rule also accepts a rule-level option:
{
"rules": {
"react-hooks/exhaustive-deps": ["warn", {
"additionalHooks": "(useMyCustomHook|useAnotherHook)"
}]
}
}
-
additionalHooks
:用于检查钩子是否存在详尽依赖的正则表达式。注意:如果指定了此规则级选项,则它优先于共享的settings
配置。¥
additionalHooks
: Regex for hooks that should be checked for exhaustive dependencies. Note: If this rule-level option is specified, it takes precedence over the sharedsettings
configuration.