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

React CodeSandbox打地鼠游戏图片不更新问题求助

问题:React打地鼠游戏状态更新但界面不渲染

在CodeSandbox开发打地鼠游戏时,界面上9张地洞图片的src属性始终不更新,尽管控制台日志显示状态已变化。

原代码

import "./styles.css";
import empty from "./hole.png";
import mole from "./mole.png";
import whacked from "./whacked.png";
import { useState } from "react";

export default function App() {
  const [holeStates, setHoleStates] = useState([
    { hole: empty },
    { hole: empty },
    { hole: empty },
    { hole: empty },
    { hole: empty },
    { hole: empty },
    { hole: empty },
    { hole: empty },
    { hole: empty }
  ]);

  function start() {
    interval = window.setInterval(moveMole, 1000);
  }

  //why is "clear interval not working?"
  function stop() {
    clearInterval(interval);
  }

  function moveMole() {
    //this should reset all holes back to empty, ensuring
    //two moles won't appear at once.

    for (x of holeStates) {
      x.hole = empty;
    }

    //this should give us a random number representing holes 1 - 9
    let randomHoleNum = Math.floor(Math.random() * 9);

    var copyArray = holeStates;

    copyArray[randomHoleNum] = { hole: mole };

    setHoleStates(copyArray);

    console.log(holeStates);
    //judging by the console log this seems to be working,
    //but the image doesn't change. Why?
  }

  return (
    <div className="App">
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
      <br />
      <br />
      <img src={holeStates[0].hole} />
      <img src={holeStates[1].hole} />
      <img src={holeStates[2].hole} />
      <br />
      <img src={holeStates[3].hole} />
      <img src={holeStates[4].hole} />
      <img src={holeStates[5].hole} />
      <br />
      <img src={holeStates[6].hole} />
      <img src={holeStates[7].hole} />
      <img src={holeStates[8].hole} />
    </div>
  );
}

问题根源

React的状态更新依赖不可变性——只有当你传入全新的状态值(而非修改原状态的引用)时,React才会检测到变化并触发组件重渲染。你的代码犯了两个关键错误:

  1. 原地修改原状态数组:
    for (x of holeStates) {x.hole = empty;} 直接修改了状态数组中对象的属性,属于原地修改,React无法追踪这种变化。

  2. 未创建新数组引用:
    var copyArray = holeStates; 只是把原数组的引用赋值给新变量,本质上还是同一个数组。调用setHoleStates(copyArray)时,React对比前后状态的引用,发现没有变化,所以不会触发重渲染。

另外,interval使用全局变量会导致组件卸载后内存泄漏,且stop函数可能无法正确清除定时器,需要用useRef保存定时器ID。

修复方案与完整代码

修改moveMole函数,每次创建全新的数组和对象;用useRef管理定时器ID:

import "./styles.css";
import empty from "./hole.png";
import mole from "./mole.png";
import whacked from "./whacked.png";
import { useState, useRef } from "react";

export default function App() {
  const [holeStates, setHoleStates] = useState(
    Array(9).fill({ hole: empty })
  );
  const intervalRef = useRef(null);

  function start() {
    // 避免重复创建定时器
    if (intervalRef.current) return;
    intervalRef.current = window.setInterval(moveMole, 1000);
  }

  function stop() {
    clearInterval(intervalRef.current);
    intervalRef.current = null;
  }

  function moveMole() {
    // 创建全新的数组,所有地洞重置为空
    const newHoleStates = Array(9).fill({ hole: empty });
    // 随机选择一个地洞设置为mole
    const randomHoleNum = Math.floor(Math.random() * 9);
    newHoleStates[randomHoleNum] = { hole: mole };
    // 更新状态
    setHoleStates(newHoleStates);
  }

  return (
    <div className="App">
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
      <br />
      <br />
      {holeStates.map((state, index) => (
        <img key={index} src={state.hole} alt={`Hole ${index + 1}`} />
      ))}
    </div>
  );
}

关键修改点

  • 使用Array(9).fill({ hole: empty })简化初始状态的创建。
  • moveMole中创建全新的数组newHoleStates,避免修改原状态。
  • 用useRef保存intervalRef,确保定时器ID在组件生命周期内稳定,且能正确清除。
  • 使用map渲染图片,代码更简洁,同时添加key属性符合React列表渲染规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 11:52:46