如何解决React类组件转函数组件时useEffect出现无限循环的问题
问题原因与修复方案
你遇到的异常核心是函数组件闭包陷阱导致的:你给useEffect设置了空依赖数组,初始化创建定时器后,定时器回调里的moveSnake永远捕获的是首次渲染时的snake、direction状态值,永远拿不到最新状态。如果你之前为了拿到最新值把snake、moveSnake加到了useEffect依赖中,就会每次setSnake更新状态后触发useEffect重新执行,反复创建销毁定时器,最终出现无限循环的问题。
具体修复步骤
- 第一步:将定时器、事件回调需要用到的可变状态用
useRef包裹,保证随时可以读取到最新值
// 存储direction最新值 const directionRef = useRef(direction); // direction变化时同步更新ref useEffect(() => { directionRef.current = direction; }, [direction]); // 如果onKeyDown也依赖最新状态,同样用ref存储最新的onKeyDown const onKeyDownRef = useRef(onKeyDown); useEffect(() => { onKeyDownRef.current = onKeyDown; }, [onKeyDown]);
- 第二步:修改
moveSnake的状态更新为函数式写法,直接获取最新的snake状态,规避闭包捕获旧值问题
const moveSnake = () => { setSnake(prevSnake => { const dots = [...prevSnake]; const head = dots[dots.length - 1]; let newHead; switch(directionRef.current){ case "RIGHT": newHead = [head[0] + 2, head[1]]; break; case "LEFT": newHead = [head[0] - 2, head[1]]; break; case "UP": newHead = [head[0], head[1] - 2]; break; case "DOWN": newHead = [head[0], head[1] + 2]; break; default: newHead = head; } dots.push(newHead); dots.shift(); console.log(dots); return dots; }) }
- 第三步:调整
useEffect的逻辑,卸载时同时解绑键盘事件,避免内存泄漏
useEffect(()=>{ document.onkeydown = (e) => onKeyDownRef.current(e); const interval = setInterval(() => { moveSnake() }, 200); return () => { clearInterval(interval); document.onkeydown = null; }; },[]);
如果需要动态调整蛇的移动速度,只需要把速度值也用useRef存储,或者加到useEffect的依赖数组中,速度变化时会自动销毁旧定时器、创建新的定时器即可。
内容的提问来源于stack exchange,提问作者fresh_developer
相关产品推荐
相关产品推荐

