函数未使用挂钩指向状态变量的更新值

时间:2020-04-13 08:29:39

标签: javascript reactjs

我目前正在将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

但是输出看起来像这样:

enter image description here

为什么未记录更新的num变量? logNum()函数不应该始终指向状态的当前值吗?

1 个答案:

答案 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);