
规则:exhaustive-deps
ReactAPI 参考:ESLint 插件
exhaustive-deps
验证 React hooks 的依赖数组是否包含所有必要的依赖。
规则详情 {/rule-details/}
useEffect、useMemo 和 useCallback 等 React hooks 接受依赖数组。当这些 hooks 内部引用的值未包含在依赖数组中时,React 不会在该依赖变化时重新运行 effect 或重新计算值。这会导致闭包过期(stale closure),使 hook 使用过时的值。
常见违规 {/common-violations/}
这种错误通常发生在你试图“欺骗” React 的依赖关系以控制 effect 何时运行时。Effects 应该将你的组件与外部系统同步。依赖数组告诉 React effect 使用了哪些值,以便 React 知道何时需要重新同步。
如果你发现自己与 linter 对抗,很可能需要重构代码。请参阅移除 Effect 依赖了解如何操作。
无效用法 {/invalid/}
本规则的错误代码示例:
js
// ❌ 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/}
本规则的正确代码示例:
js
// ✅ All dependencies included
useEffect(() => {
console.log(count);
}, [count]);
// ✅ All dependencies included
useEffect(() => {
fetchUser(userId);
}, [userId]);
故障排除 {/troubleshooting/}
添加函数依赖会导致无限循环 {/function-dependency-loops/}
你有一个 effect,但你在每次渲染时都创建一个新函数:
js
// ❌ Causes infinite loop
const logItems = () => {
console.log(items);
};
useEffect(() => {
logItems();
}, [logItems]); // Infinite loop!
在大多数情况下,你并不需要这个 effect。在动作实际发生的地方调用该函数即可:
js
// ✅ 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);
});
如果你确实需要这个 effect(例如,订阅某个外部资源),请让依赖稳定:
js
// ✅ 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]);
只运行一次 effect {/effect-on-mount/}
你想在挂载时只运行一次 effect,但 linter 抱怨缺少依赖:
js
// ❌ Missing dependency
useEffect(() => {
sendAnalytics(userId);
}, []); // Missing 'userId'
要么包含该依赖(推荐),要么在确实需要只运行一次时使用 ref:
js
// ✅ 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 settings 来配置自定义 effect hooks(在 eslint-plugin-react-hooks 6.1.1 及更高版本中可用):
js
{
"settings": {
"react-hooks": {
"additionalEffectHooks": "(useMyEffect|useCustomEffect)"
}
}
}
additionalEffectHooks:正则表达式,匹配应检查完整依赖的自定义 hooks。此配置在所有react-hooks规则之间共享。
为了向后兼容,本规则也接受规则级选项:
js
{
"rules": {
"react-hooks/exhaustive-deps": ["warn", {
"additionalHooks": "(useMyCustomHook|useAnotherHook)"
}]
}
}
additionalHooks:用于检查完整依赖的 hooks 正则表达式。注意: 如果指定了此规则级选项,则它优先于共享的settings配置。
帮助我们改进文档
发现翻译问题或内容错误?请告诉我们。
