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
相关产品推荐
相关产品推荐

