为何这段React代码首次执行跳过索引1?控制台输出异常解析
问题
以下是我的React代码:
const [index, setIndex] = useState(0); useEffect(()=>{ if (index === 3){ setIndex(0) console.log(index) }else{ setTimeout(() => setIndex((index) => index + 1), 2000); console.log(index) } }, [index]);
我的问题是:为何这段代码首次执行时会跳过索引1?控制台输出结果为0 0 2 3 0 1 2 3 0,其中前两个0的输出逻辑我可以理解,但无法理解为何会出现2,后续的3 0 1 2等输出序列符合预期。我的需求是根据数组索引在DOM中切换显示对应的名称。
原因分析
问题出在React 18开发环境的严格模式上:
- React 18严格模式会在开发阶段让组件挂载时重复执行一次
useEffect(模拟卸载后重新挂载的场景),导致你设置了两个同时在2秒后触发的setTimeout。 - 2秒后,两个定时器依次执行:第一个把
index从0更新到1,第二个紧接着把index从1更新到2。 - 虽然
index=1时useEffect确实会执行并打印1,但状态更新太快,要么控制台日志被后续输出覆盖,要么视觉上直接跳过了index=1的DOM显示,让你觉得索引1被跳过了。
修复方案
核心是确保同一时间只有一个定时器运行,用useRef保存定时器ID,并在useEffect的清理函数中清除旧定时器:
import { useState, useEffect, useRef } from 'react'; const [index, setIndex] = useState(0); const timerRef = useRef(null); useEffect(() => { if (index === 3) { setIndex(0); console.log(index); } else { // 清除上一个定时器,避免多个定时器同时触发 if (timerRef.current) { clearTimeout(timerRef.current); } // 保存新定时器ID timerRef.current = setTimeout(() => { setIndex(prev => prev + 1); }, 2000); console.log(index); } // 组件卸载或依赖变化时清除当前定时器 return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, [index]);
修改后,每次index变化都会先清除旧定时器,确保只有一个定时器在计时,索引会按0→1→2→3→0的顺序正常切换,不会再出现跳索引的问题。
内容的提问来源于stack exchange,提问作者Habizsabi
相关产品推荐
相关产品推荐

