React useCallback依赖变量两种声明方式的行为差异疑问
问题原因解答
核心本质
两种实现的差异是React函数组件渲染机制和JS闭包捕获规则共同作用的结果:
- 闭包捕获的是变量的引用,而非变量创建时的瞬时值
- 每次React组件重新渲染,都会生成一个独立的作用域,所有作用域内的变量/函数都会重新声明
第一种useState实现的逻辑
你之前的理解完全正确:
useState返回的temp是当前渲染周期内的只读常量,每次渲染都会生成一个全新的temp常量,和之前渲染的temp是完全独立的变量- 你的
useCallback依赖数组为空,所以只会在首次渲染时生成一次,它的闭包捕获的是首次渲染作用域下的temp常量,值永远是true,不会跟随后续渲染的temp变化,所以点击永远会触发num加1。
对应代码:
const [temp, setTemp] = useState(true); setTimeout(() => setTemp(false), 2000); const [num, setNum] = useState(0); const callbackButtonHandler = useCallback(() => { if (temp) { setNum((prevNum) => prevNum + 1); } }, []);
第二种普通let变量实现的逻辑
你观察到的现象确实和闭包直接相关:
- 你没有用
useState,所以setTimeout修改temp的时候不会触发组件重渲染,整个组件函数只会执行一次,整个生命周期里只会生成一个temp变量 useCallback捕获的是这个唯一的temp变量的引用,2秒后setTimeout把这个变量的值修改为false,后续callbackButtonHandler执行时,访问到的就是已经修改后的temp值,所以判断不生效,num不会增加。
对应代码:
// const [temp, setTemp] = useState(true); // setTimeout(() => setTemp(false), 2000); let temp = true; setTimeout(() => (temp = false), 2000); // 其余逻辑不变
额外注意
不要在React组件作用域内直接用普通let声明需要响应变化或者在回调中使用的可变变量:
- 如果没有触发组件重渲染,变量的修改React感知不到,不会触发视图更新
- 如果触发了组件重渲染,变量会被重新初始化,之前的修改全部丢失
如果需要保存不需要触发视图更新的可变值,推荐使用useRef;需要触发视图更新的值用useState,同时对应状态要加到useCallback的依赖数组中才能拿到最新值。
内容的提问来源于stack exchange,提问作者chaukhoa97
相关产品推荐
相关产品推荐

