You need to enable JavaScript to run this app. React中如何实现内容仅渲染一次?战舰游戏船只生成无限循环问题
优惠活动
大模型
产品
解决方案
定价
更多

React中如何实现内容仅渲染一次?战舰游戏船只生成无限循环问题

问题根因分析
  • 无限循环来自状态更新的逻辑冲突:你把船组重试逻辑放在了依赖playerShipsArray的useEffect中,每次调用generateShips更新该状态都会重新触发这个useEffect,加上React的setState是异步更新,while循环读取的始终是当前闭包中的旧状态值,永远拿不到更新后的船组数据,自然会进入死循环。
  • generateShips函数逻辑冗余错误:内部while循环判断数组长度小于5,但嵌套的for循环一次就会往数组推入5条船数据,while循环实际没有任何作用。
  • 直接修改state原值:placeShipsOnBoard直接修改playerBoard这个state对象,不符合React不可变状态的更新规则,会导致组件不会触发对应重渲染,页面棋盘不会同步更新。
修正方案

将船组生成、碰撞校验的逻辑合并到同一个函数内,不需要通过状态变化触发重试,在单次函数执行中完成所有校验,拿到合法船组后再一次性更新状态,同时修正棋盘更新逻辑,修正后代码如下:

const GameControl = () => {    
    const [playerBoard, setPlayerBoard] = useState(() => GameBoard('player'));    
    const [playerShipsArray, setPlayerShipsArray] = useState([]);    

    // 初始化游戏仅执行一次
    useEffect(() => {
        // 生成无碰撞的合法船组
        const validShips = generateValidShips()
        setPlayerShipsArray(validShips)
        // 不可变方式更新棋盘,先创建新的棋盘实例再更新状态
        const newBoard = GameBoard('player')
        placeShipsOnBoard(validShips, newBoard)
        setPlayerBoard(newBoard)
    }, [])    

    // 合并生成+校验逻辑,直接返回合法船组
    function generateValidShips() {
        let shipsArr = [];
        // 循环重试直到生成无碰撞的船组
        while(true) {
            shipsArr = []
            // 生成5艘不同长度的船
            for(let i = 1; i < 6; i++){
                let startCoord = Math.floor(Math.random() * 100);
                const ship = Ship(i, i, isVertical(), startCoord)
                shipsArr.push(ship)
            }
            // 校验无碰撞直接返回结果
            if(checkTakenCells(shipsArr)) {
                return shipsArr
            }
        }
    }

    function checkTakenCells (array){
        let takenCells = [];
        array.forEach(ship => takenCells.push(...ship.takenCells()))
        return new Set(takenCells).size === takenCells.length
    }    

    function placeShipsOnBoard(array, board){
        for (let i = 0; i < array.length; i++){
            board.placeShip(array[i], array[i].startCoord) 
        }
    }      

    return (
        <>
          {/* 遍历playerBoard生成10x10网格的逻辑 */}
        </>
    )
}
核心改动说明
  1. 移除了依赖playerShipsArray的useEffect,彻底避免状态更新触发的循环逻辑
  2. 新增generateValidShips函数,在单次函数调用内完成船组生成和碰撞校验,直到拿到合法船组才返回,完全不需要依赖组件重渲染来重试
  3. 棋盘更新时先创建新的board实例,更新完成后再调用setPlayerBoard替换旧状态,符合React不可变状态的更新要求,能正常触发组件重渲染同步页面内容

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.20 05:06:03