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

如何解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 18:54:06