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才会检测到变化并触发组件重渲染。你的代码犯了两个关键错误:
原地修改原状态数组:
for (x of holeStates) {x.hole = empty;}直接修改了状态数组中对象的属性,属于原地修改,React无法追踪这种变化。未创建新数组引用:
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
相关产品推荐
相关产品推荐

