React中触发setTimeout:游戏获胜后延迟重置状态
解决React Tic-Tac-Toe获胜后延迟3秒重置状态的问题
嘿,我完全懂你的需求!你不需要在组件挂载时就触发延迟重置,只需要在calculateWinner检测到获胜的那一刻启动定时器就好。咱们直接把定时器逻辑嵌在获胜判定的流程里就行,下面结合tic-tac-toe的示例给你具体实现方案:
核心思路
- 先写一个专门的
resetGame函数,用来把游戏状态(棋盘、当前玩家等)恢复到初始值 - 在处理落子的函数中,每次落子后调用
calculateWinner检查是否产生赢家 - 一旦检测到赢家,就用
setTimeout延迟3秒调用resetGame - 记得清理定时器,避免组件卸载后定时器还在执行导致内存泄漏
具体代码实现(函数组件版本)
import { useState, useRef, useEffect } from 'react'; function TicTacToe() { const [board, setBoard] = useState(Array(9).fill(null)); const [currentPlayer, setCurrentPlayer] = useState('X'); // 用useRef保存定时器ID,方便后续清除 const timeoutId = useRef(null); // 重置游戏状态的函数 const resetGame = () => { setBoard(Array(9).fill(null)); setCurrentPlayer('X'); }; // 组件卸载时清除未执行的定时器,防止内存泄漏 useEffect(() => { return () => { if (timeoutId.current) { clearTimeout(timeoutId.current); } }; }, []); // 官方示例的获胜判定函数 const calculateWinner = (squares) => { const lines = [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 3, 6], [1, 4, 7], [2, 5, 8], [0, 4, 8], [2, 4, 6], ]; for (let i = 0; i < lines.length; i++) { const [a, b, c] = lines[i]; if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) { return squares[a]; } } return null; }; const handleClick = (index) => { // 先检查当前位置是否已被占用,或者已经有赢家,直接终止操作 const currentWinner = calculateWinner(board); if (board[index] || currentWinner) { return; } // 执行落子逻辑 const updatedBoard = [...board]; updatedBoard[index] = currentPlayer; setBoard(updatedBoard); // 检查落子后是否产生赢家 const newWinner = calculateWinner(updatedBoard); if (newWinner) { // 启动3秒延迟重置的定时器 timeoutId.current = setTimeout(() => { resetGame(); }, 3000); } else { // 没有赢家则切换玩家 setCurrentPlayer(prev => prev === 'X' ? 'O' : 'X'); // 清除之前可能存在的未执行定时器(比如中途落子打断了之前的延迟) if (timeoutId.current) { clearTimeout(timeoutId.current); timeoutId.current = null; } } }; // 渲染棋盘等UI部分(这里省略官方示例的渲染代码) return ( <div className="game"> {/* 你的棋盘渲染逻辑 */} </div> ); } export default TicTacToe;
关键细节说明
- 定时器的保存与清理:用
useRef存储定时器ID,这样即使组件重新渲染,也能拿到同一个定时器引用。在组件卸载的清理函数中清除定时器,避免内存泄漏。 - 中途清除定时器:如果在延迟重置的3秒内,玩家又进行了操作(虽然我们已经在
handleClick开头判断了有赢家就返回,但如果是平局或者其他情况),需要清除之前的定时器,避免不必要的状态重置。 - 类组件适配:如果是用类组件开发,逻辑类似,只需要把定时器ID存在
this.timeoutId中,然后在componentWillUnmount生命周期里执行clearTimeout(this.timeoutId)即可。
内容的提问来源于stack exchange,提问作者Jan Cziżikow
相关产品推荐
相关产品推荐

