React开发Blackjack游戏:while循环中useState引发无限循环
问题原因分析
- React State异步更新导致循环条件失效:
setDealerCards是异步操作,在while循环中调用时,dealerCards的状态不会立即更新,循环条件getHandValue(dealerCards) < 17始终基于初始值判断,永远为真,触发无限循环。 - 牌堆每次渲染重复洗牌:
shuffledDeck直接在组件顶层定义,组件每次重新渲染都会执行shuffleDeck(deck),导致牌堆被反复打乱,取牌逻辑完全混乱。 - 取牌索引逻辑错误:循环中取牌依赖
dealerCards.length,但由于state未更新,每次取的都是同一个位置的牌,即使state能更新,也会重复取同一张牌。
解决方案
1. 修复牌堆洗牌逻辑,避免重复打乱
使用useMemo缓存洗牌后的牌堆,确保仅在组件初始化时洗牌一次:
import { useMemo, useState } from 'react'; // ...其他代码 const shuffledDeck = useMemo(() => shuffleDeck([...deck]), []);
同时修改shuffleDeck为纯函数,避免修改原数组:
const shuffleDeck = (deck: Card[]): Card[] => { const newDeck = [...deck]; // 复制原数组,避免污染输入 for (let i = newDeck.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [newDeck[i], newDeck[j]] = [newDeck[j], newDeck[i]]; } return newDeck; };
2. 重构庄家抽牌逻辑,避免异步State循环
用临时变量同步计算庄家最终手牌,一次性更新State,彻底解决循环问题:
const handleStand = () => { if (gameState !== 'playing') return; // 用临时变量跟踪庄家手牌,同步更新,不依赖异步State let currentDealerCards = [...dealerCards]; // 计算当前已用牌的数量,确定下一张牌的索引 let nextCardIndex = playerCards.length + currentDealerCards.length; // 循环抽牌直到手牌值≥17,同时避免牌堆耗尽 while (getHandValue(currentDealerCards) < 17 && nextCardIndex < shuffledDeck.length) { currentDealerCards.push(shuffledDeck[nextCardIndex]); nextCardIndex++; } // 一次性更新庄家手牌State setDealerCards(currentDealerCards); // 计算胜负结果 const dealerHandValue = getHandValue(currentDealerCards); const playerHandValue = getHandValue(playerCards); if (dealerHandValue > 21 || dealerHandValue < playerHandValue) { setGameState('won'); setPlayerBalance(prev => prev + currentBet); // 用函数式更新确保取到最新余额 } else if (dealerHandValue > playerHandValue) { setGameState('lost'); setPlayerBalance(prev => prev - currentBet); } else { setGameState('tied'); } };
补充说明
- 使用函数式更新
setPlayerBalance(prev => ...)是为了确保基于最新的余额状态计算,避免因State异步更新导致的计算错误。 - 增加
nextCardIndex < shuffledDeck.length的判断,防止牌堆耗尽时继续取牌导致报错。
内容的提问来源于stack exchange,提问作者Jump3rr9
相关产品推荐
相关产品推荐

