我目前正在将React应用从setState重构为钩子。我不明白为什么状态变量没有改变。这是一个示例:
import React, { useState, useEffect } from 'react';
function Hook() {
const [num, setNum] = useState(1);
useEffect(() => {
window.addEventListener("mousemove", logNum);
}, []);
const logNum = () => {
console.log(num);
}
const handleToggle = () => {
if (num == 1) {
console.log('setting num to 2');
setNum(2);
} else {
console.log('setting num to 1');
setNum(1);
}
}
return (
<div>
<button onClick={handleToggle}>TOGGLE BOOL</button>
</div>
);
}
export default Hook;
当我单击按钮时,我期望输出为:
// 1
// setting num to 2
// 2
// setting num to 1
// 1
但是输出看起来像这样:
为什么未记录更新的num变量? logNum()函数不应该始终指向状态的当前值吗?
答案 0 :(得分:2)
这就是为什么效果依赖必须<穷>详尽。 Don't lie about dependencies。
logNum
覆盖num
,因此在每次重新渲染时,都有一个包含新值的新num
变量和一个记录该值的新logNum
函数。但是,您的效果仅初始化一次,因此它只知道第一个logNum
。因此,您必须添加logNum
作为依赖项,以便只要num
并因此logNum
发生变化,效果就会得到更新:
useEffect(() => {
window.addEventListener("mousemove", logNum);
}, [logNum]);
您会注意到您的效果未正确clean up,您也应该添加一个removeEventListener
。
return () => window.removeEventListener("mousemove", logNum);
现在,如果您调试这段代码,您会注意到效果会在每次重新渲染时触发。这是因为无论logNum
是否更改,每次重新渲染都会创建一个新的num
函数。为防止这种情况,您可以使用useCallback
使logNum
引用稳定:
const logNum = useCallback(() => console.log(num), [num]);
所有这些的替代方案是使用对current state的引用:
const actualNum = useRef(num);
// that works no matter when and how this is executed
console.log(actualNum.current);