javascript 为什么在严格模式下使用setTimeout和React对Map进行两次更新

djmepvbi  于 2022-11-27  发布在  Java
关注(0)|答案(2)|浏览(106)

我有以下React组件:
App.tsx:

function App() {
    const [countdownTimers, setCountdownTimers] = React.useState<
        Map<number, number>
    >(new Map([[1, 60]]));

    useEffect(() => {
        const timeoutId = setInterval(() => {
            setCountdownTimers((prevState) => {
                console.log(prevState);
                for (const [timerKey, timer] of prevState) {
                    prevState.set(timerKey, timer - 1);
                }
                return new Map(prevState);
            });
        }, 1000);
        return () => {
            clearInterval(timeoutId);
        };
    }, []);

    return <>{countdownTimers.get(1)}</>;
};

index.tsx

<React.StrictMode>
    <App />
</React.StrictMode>

上面代码应该每秒从Map中所有值中减去1但是由于StrictMode,它减去了2删除<React.StrictMode>解决了这个问题,但是我想了解为什么StrictMode只有在Map情况下才会这样
你能告诉我为什么是这样吗?

mspsb9vt

mspsb9vt1#

strict mode中,状态更新器函数被调用两次,以尝试检测可能的错误。
这里的代码确实有一个值得商榷的bug --您正在 * 改变 * Map中的现有状态:

setCountdownTimers((prevState) => {
    console.log(prevState);
    for (const [timerKey, timer] of prevState) {
        prevState.set(timerKey, timer - 1);
    }
    return new Map(prevState);
});

虽然在返回时创建了一个新的Map,但仍然调用了prevState.set--改变它。这意味着(严格)状态更新程序第二次运行时,它看到的Map(第二次在prevState中)已经将其值减少了一次。
不要改变现有的Map,而是立即创建新的Map,并且只更改该新Map。
第一次

evrscar2

evrscar22#

发生这种情况是因为React strict模式,它与Map数据结构没有任何关系。
严格模式不能自动检测副作用,但它可以通过使副作用更具有确定性来帮助你发现它们。这可以通过有意地双重调用以下函数来实现:

  • 类组件构造函数、render和shouldComponentUpdate方法
  • 类组件静态getDerivedStateFromProps方法
  • 功能组件主体
    *状态更新程序函数(setState的第一个参数)
  • 传递给useState、useMemo或useReducer的函数

实际上,传递给setCountdownTimers setter的回调函数被调用了两次,因此减去了2而不是1

相关问题