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

React.js单键重置/重启秒表开发:实现定时器启停功能

React 迁移:实现带重置/停止功能的定时器游戏

原项目逻辑(原生JavaScript)

这是一款数字判断小游戏,核心规则如下:

  • 点击启动按钮后,生成1-1000的随机数,启动每10ms递增计数的定时器(最多300次,对应3秒)
  • 两个操作按钮:
    • 点击「是3的倍数」:若随机数符合条件,累加当前计数到总分,重置计数并生成新随机数;否则停止定时器,游戏结束
    • 点击「不是3的倍数」:若随机数符合条件,累加当前计数到总分,重置计数并生成新随机数;否则停止定时器,游戏结束
  • 3秒内未操作则自动结束游戏,显示完成的数字数量和总耗时(秒)

原核心代码:

let timer = false;
let rando = null;
let conta = 0;
let punteggio = 0;

startup.addEventListener("click", () => {
    timer = true

    rando = Math.ceil(Math.random()*1000)
    centrato.innerText = rando

    challenge()
})

function challenge(){
    if(timer && conta < 300){
        conta ++
        setTimeout(challenge, 10)
    }else if(conta == 300){
        perso()
    }else{
        perso()
    }
}

操作按钮及结果处理代码:

tre.addEventListener("click", () => {
    if( rando% 3 == 0 ){
        vinto()
    }else{
        timer = false
    }
})

notre.addEventListener("click", () => {
    if(rando% 3 == 0){
        timer = false
    }else{
        vinto()
    }
})

function vinto(){
    punteggio += conta
    conta = 0;
    rando = Math.ceil(Math.random()*1000)
}

function perso(){
    risultato.innerText = "You got " + giri + " numbers in " + punteggio/100 + " seconds"
    punteggio = 0;
    conta = 0;
}

迁移到React的问题

尝试用React的useState和useEffect实现时,遇到核心问题:无法同时实现「单键点击重置并重复执行定时器」和「点击按钮停止定时器」的功能,试用setInterval、双击重置等方案均未达到预期。

React实现方案

以下是完整的React组件代码,满足所有需求:

import { useState, useEffect, useRef } from 'react';

function NumberChallengeGame() {
  // 游戏状态管理
  const [isRunning, setIsRunning] = useState(false);
  const [randomNum, setRandomNum] = useState(0);
  const [count, setCount] = useState(0);
  const [score, setScore] = useState(0);
  const [rounds, setRounds] = useState(0);
  const [result, setResult] = useState('');
  
  // 存储定时器ID,用于清理定时器
  const timerRef = useRef(null);

  // 生成1-1000的随机数
  const generateRandomNum = () => {
    setRandomNum(Math.ceil(Math.random() * 1000));
  };

  // 启动/重置游戏
  const startGame = () => {
    setIsRunning(true);
    setCount(0);
    setResult('');
    generateRandomNum();
  };

  // 停止游戏并显示结果
  const stopGame = () => {
    setIsRunning(false);
    setResult(`你完成了 ${rounds} 个数字,总耗时 ${score / 100} 秒`);
    setScore(0);
    setRounds(0);
    setCount(0);
  };

  // 处理正确判断逻辑
  const handleWin = () => {
    setScore(prev => prev + count);
    setRounds(prev => prev + 1);
    setCount(0);
    generateRandomNum();
  };

  // 处理错误判断逻辑
  const handleLose = () => {
    stopGame();
  };

  // 「是3的倍数」按钮点击逻辑
  const handleMultipleOfThree = () => {
    if (isRunning) {
      randomNum % 3 === 0 ? handleWin() : handleLose();
    }
  };

  // 「不是3的倍数」按钮点击逻辑
  const handleNotMultipleOfThree = () => {
    if (isRunning) {
      randomNum % 3 !== 0 ? handleWin() : handleLose();
    }
  };

  // 定时器核心逻辑:每10ms计数一次,达到300次自动结束
  useEffect(() => {
    if (isRunning) {
      timerRef.current = setTimeout(() => {
        setCount(prev => {
          if (prev >= 299) {
            stopGame();
            return 0;
          }
          return prev + 1;
        });
      }, 10);
    }

    // 清理函数:组件卸载或运行状态变化时清除定时器
    return () => {
      if (timerRef.current) {
        clearTimeout(timerRef.current);
      }
    };
  }, [isRunning, count]);

  return (
    <div style={{ padding: '20px', textAlign: 'center' }}>
      <h2>数字挑战游戏</h2>
      <div style={{ fontSize: '2rem', margin: '20px 0' }}>
        {isRunning ? randomNum : '点击开始游戏'}
      </div>
      <div style={{ margin: '10px 0' }}>
        剩余时间: {(3 - count / 100).toFixed(2)} 秒
      </div>
      <button 
        onClick={startGame} 
        disabled={isRunning}
        style={{ padding: '10px 20px', margin: '0 10px' }}
      >
        开始/重置
      </button>
      <button 
        onClick={stopGame} 
        disabled={!isRunning}
        style={{ padding: '10px 20px', margin: '0 10px' }}
      >
        停止游戏
      </button>
      <div style={{ margin: '20px 0' }}>
        <button 
          onClick={handleMultipleOfThree} 
          disabled={!isRunning}
          style={{ padding: '10px 20px', margin: '0 10px' }}
        >
          是3的倍数
        </button>
        <button 
          onClick={handleNotMultipleOfThree} 
          disabled={!isRunning}
          style={{ padding: '10px 20px', margin: '0 10px' }}
        >
          不是3的倍数
        </button>
      </div>
      <div style={{ fontSize: '1.2rem', marginTop: '20px' }}>
        {result}
      </div>
    </div>
  );
}

export default NumberChallengeGame;

关键实现说明

  1. 状态管理:通过useState统一管理游戏运行状态、随机数、计数、分数等核心数据
  2. 定时器控制:用useRef存储定时器ID,确保组件更新时能正确清除旧定时器,避免内存泄漏
  3. Effect依赖:监听isRunning和count状态变化,动态启动/清理定时器
  4. 按钮逻辑:根据游戏运行状态禁用/启用按钮,分支处理正确/错误判断的流程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 14:02:58