You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React严格模式下空依赖useEffect触发两次致加载动画异常

解决React严格模式下Connect4回放动画重复执行的问题

我编写了以下代码,用于加载Connect4游戏的历史状态并通过动画重现:

useEffect(() => {
    const history = historyFromStateURL();

    (async () => {
        for (const colIndex of history) {
            await sleep(100);

            dispatch({
                type: 'CLICK',
                colIndex,
                isReplay: true,
            });
        }
    })();
}, []);

问题在于该代码存在副作用:若effect执行两次,会派发两倍数量的点击事件,导致组件状态异常。由于使用了React严格模式,恰好触发了这种情况。原本以为useEffect会在组件生命周期内仅执行一次,但React严格模式的开发环境下并非如此。


可行的修改方案

方案一:通过清理函数终止未完成的异步任务

React严格模式在开发环境中会重复执行effect,以此检测未正确处理的副作用。我们可以在effect中添加一个取消标志,配合清理函数终止未完成的异步循环:

useEffect(() => {
    const history = historyFromStateURL();
    let isCancelled = false;

    (async () => {
        for (const colIndex of history) {
            if (isCancelled) break;
            await sleep(100);
            if (isCancelled) break;

            dispatch({
                type: 'CLICK',
                colIndex,
                isReplay: true,
            });
        }
    })();

    // 组件卸载或effect重新执行时触发,标记任务取消
    return () => {
        isCancelled = true;
    };
}, []);

方案二:用Ref标记回放是否已执行

如果确认回放逻辑只需执行一次,不管effect触发多少次,可以使用useRef记录执行状态,避免重复触发:

const hasReplayed = useRef(false);

useEffect(() => {
    if (hasReplayed.current) return;

    const history = historyFromStateURL();

    (async () => {
        for (const colIndex of history) {
            await sleep(100);

            dispatch({
                type: 'CLICK',
                colIndex,
                isReplay: true,
            });
        }
        hasReplayed.current = true;
    })();
}, []);

额外说明

React严格模式的重复执行行为仅存在于开发环境,生产环境下useEffect会按照预期只执行一次。上述两种方案都能兼容开发与生产环境,解决重复派发事件的问题。

内容的提问来源于stack exchange,提问作者Teiem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 06:33:11