
在 react 中使用 usecallback 包裹状态切换函数时,若未将相关状态变量作为依赖项传入,会导致闭包捕获初始值,使状态更新失效——这是常见但易被忽视的陷阱。
在 react 中使用 usecallback 包裹状态切换函数时,若未将相关状态变量作为依赖项传入,会导致闭包捕获初始值,使状态更新失效——这是常见但易被忽视的陷阱。
当你在 useCallback 中引用外部状态(如 dark)却未将其加入依赖数组时,React 会缓存该函数的首次创建版本。由于 dark 的初始值为 true,!dark 在闭包中始终计算为 false,因此 setDark(!dark) 实际上每次都执行 setDark(false),导致第二次点击后状态不再变化。
✅ 正确写法是将 dark 显式添加为 useCallback 的依赖项:
import React from 'react';
import { useState, useCallback } from 'react';
export default function App() {
const [dark, setDark] = useState(true);
// ✅ 正确:dark 作为依赖项,确保每次 dark 变化时生成新函数
const toggleDark = useCallback(() => {
setDark(prev => !prev); // 推荐使用函数式更新,避免依赖 stale closure
}, [dark]); // 或更安全地省略依赖项(见下文)
return (
<main><p onclick="{toggleDark}">{dark ? 'dark mode' : 'light mode'}</p>
</main>
);
}
⚠️ 注意事项:
React 与 Next.js 性能优化指南,源自 Vercel 工程团队。适用于编写、审查或重构 React/Next.js 代码时使用。
-
优先使用函数式更新(
setDark(prev => !prev)):它不依赖外部变量,可完全规避依赖项管理问题,推荐作为默认实践; - 若坚持使用
!dark形式,则dark必须出现在依赖数组中,否则触发exhaustive-depsESLint 警告且逻辑错误; -
useCallback在此处并非必需——对于简单内联事件处理器(如本例),直接写onClick={() => setDark(prev => !prev)}更简洁、无闭包风险,性能影响可忽略; - 若组件被 memo 包裹且需稳定函数引用,再考虑
useCallback,并务必配对更新依赖项。
? 总结:状态切换逻辑应优先采用函数式更新形式,既消除依赖管理负担,又保证状态更新的确定性与可预测性。










