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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 04:53:12