React中如何通过条件触发自动停止计时器(无需停止按钮)
问题
我已经实现了一个带开始、停止按钮的简易秒表,同时开发了一款基础颜色匹配游戏:
- 初始生成5个随机颜色的方块(从7种预设颜色中选)
- 点击方块会切换颜色
- 当5个方块颜色全部匹配时,页面显示提示信息并禁用方块点击
- 点击重置按钮可生成新的随机颜色组,同时统计点击次数
现在想给这个匹配游戏添加计时功能:点击开始按钮启动计时器,当5个方块颜色全部匹配时自动停止计时器(不需要停止按钮)。但尝试迁移原停止按钮的停止计时器逻辑后,出现了计时器消失或页面因过度重渲染崩溃的情况,求解决方向。
解决方向与代码调整
核心思路是监听游戏完成状态,当状态变为完成时自动停止计时器,同时优化逻辑避免不必要的重渲染:
- 移除多余停止按钮:既然无需手动停止,直接在
Stopwatch组件中删除停止按钮,仅保留开始按钮。 - 自动停表逻辑:新增
useEffect监听游戏完成状态,一旦匹配成功就调用setTimerOn(false)停止计时器。 - 优化匹配判断:直接使用
colors数组进行全匹配判断,替代原代码中通过组件props提取颜色的方式,减少不必要的计算开销。 - 重置同步计时器状态:重置游戏时,除了重置时间和点击次数,还要将
timerOn设为false,防止计时器在重置后继续运行。
修改后的GroupOfSquares组件
import Stopwatch from "./Stopwatch"; import Square from "./Square"; import { useState, useEffect } from "react"; function randomPick(array) { const index = Math.floor(Math.random() * array.length); return array[index]; } export default function GroupOfSquares({ colorsArray }) { const genRandomColors = () => { let colorArrayCopy = [...colorsArray]; let newArray = []; for (let i = 0; i < 5; i++) { let randNum = Math.floor(Math.random() * colorArrayCopy.length); let splicedItem = colorArrayCopy.splice(randNum, 1)[0] newArray.push(splicedItem); } return newArray; } const [count, setCount] = useState(0); const [colors, setColors] = useState(() => genRandomColors()); const [timerOn, setTimerOn] = useState(false); const [time, setTime] = useState(0); // 计时器核心逻辑 useEffect(() => { let interval = null; if (timerOn) { interval = setInterval(() => { setTime(prevTime => prevTime + 10) }, 10) } else { clearInterval(interval) } return () => clearInterval(interval); }, [timerOn]) // 监听游戏完成状态,自动停止计时器 useEffect(() => { if (isCompleted) { setTimerOn(false); } }, [isCompleted]) // 直接通过colors数组判断是否全部匹配,提升效率 const isCompleted = colors.every(color => color === colors[0]); const squares = colors.map((color, i) => ( <Square key={i} color={color} onClick={() => { setColors((prev) => { const next = [...prev]; next[i] = randomPick(colorsArray); return next; }); setCount((prev) => prev + 1); }} /> )); const reset = () => { setCount(0); setColors(genRandomColors()); setTime(0); setTimerOn(false); // 重置时同步停止计时器 }; return ( <div className="Container"> <Stopwatch Start={() => setTimerOn(true)} Time={time} /> <div className="RowOfSquares" style={{ pointerEvents: isCompleted ? 'none' : 'auto' }} > {squares} </div> {isCompleted && <h3>匹配成功!</h3>} <div className="clicks"> <div className="numClicks"> <span>{count} 次点击</span> </div> <button onClick={reset}>重置</button> </div> </div> ); }
修改后的Stopwatch组件
export default function Stopwatch({ Start, Time }) { return ( <div> <div> <span>{("0" + Math.floor((Time / 60000) % 60)).slice(-2)}:</span> <span>{("0" + Math.floor((Time / 1000) % 60)).slice(-2)}:</span> <span>{("0" + ((Time / 10) % 100)).slice(-2)}</span> </div> <div className="buttons"> <button onClick={Start}>开始</button> </div> </div> ); }
内容的提问来源于stack exchange,提问作者user24713855
相关产品推荐
相关产品推荐

