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;
关键实现说明
- 状态管理:通过
useState统一管理游戏运行状态、随机数、计数、分数等核心数据 - 定时器控制:用
useRef存储定时器ID,确保组件更新时能正确清除旧定时器,避免内存泄漏 - Effect依赖:监听
isRunning和count状态变化,动态启动/清理定时器 - 按钮逻辑:根据游戏运行状态禁用/启用按钮,分支处理正确/错误判断的流程
内容的提问来源于stack exchange,提问作者MisterlinuZ
相关产品推荐
相关产品推荐

