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

React井字棋UI问题:实现玩家标记先显示再执行AI计算求助

React井字棋游戏UI延迟问题及解决方法

我用React开发了一款集成Minmax算法AI的井字棋游戏,包含两个核心组件:Grid.js(棋盘)和9个Case.js(方格)。用户点击方格时触发player_move(),显示玩家的叉号,同时调用Grid组件的play()方法。play()会调用AI模型计算下一步,再触发对应方格的ia_move()显示AI的圆圈。

目前游戏功能正常,但存在体验问题:玩家的叉号和AI的圆圈几乎同时显示,甚至AI的圆圈先出现,导致用户点击后叉号有延迟感,需要实现先显示玩家叉号,再执行AI的Minmax计算并显示AI圆圈。

核心代码

Case.js 核心逻辑

player_move = () => {
    // 指令1:显示玩家叉号
    this.setState({caseState: <CrossState/>, disabled:true})
    this.props.grid.play(this.props.id)
    console.log('Player Move :', this.props.id)
}

ia_move = ()=>{
    this.setState({caseState: <CircleState/>, disabled:true})
    console.log('IA Move :', this.props.id)
}

Grid.js 核心逻辑

play = (square) => {
    this.childs.current.forEach(c => {
        c.disable()            
    });
    this.state.game.play(square, this.state.game.player)
    
    if(this.state.game.eval(this.state.game.grid) == -1){
        this.setState({text: 'You Win'})
        return
    }
    if(this.state.game.getPossibleMoves(this.state.game.grid).length == 0){
        this.setState({text: 'Equality'})
        return
    }

    let ia_choice = this.state.game.minmax(0, 6, this.state.game.grid)
    // 指令2:显示AI圆圈
    this.childs.current[ia_choice-1].ia_move()
    this.state.game.play(ia_choice, this.state.game.ia)

    if(this.state.game.eval(this.state.game.grid) == 1){
        this.setState({text: 'You Lose'})
        return
    }

    this.childs.current.forEach(c => {
        c.enable()            
    });
}

更新:问题解决

通过给AI逻辑套一层0延迟的setTimeout,成功解决了该问题,修改后的Grid.js代码如下:

play = (square) => {
    this.childs.current.forEach(c => {
        c.disable()            
    });
    this.state.game.play(square, this.state.game.player)
    
    if(this.state.game.eval(this.state.game.grid) == -1){
        this.setState({text: 'You Win'})
        return
    }
    if(this.state.game.getPossibleMoves(this.state.game.grid).length == 0){
        this.setState({text: 'Equality'})
        return
    }

    setTimeout(()=>{
        let ia_choice = this.state.game.minmax(0, 6, this.state.game.grid)

        this.childs.current[ia_choice-1].ia_move()
        this.state.game.play(ia_choice, this.state.game.ia)

        if(this.state.game.eval(this.state.game.grid) == 1){
            this.setState({text: 'You Lose'})
            return
        }

        this.childs.current.forEach(c => {
            c.enable()            
        });
    }, 0)
    
}

原理说明

React的setState是异步更新机制,它不会立即触发DOM渲染,而是将更新请求放入队列,等待当前事件循环结束后批量处理。

原代码中,玩家触发player_move()调用setState后,立刻执行Grid的play()方法,其中的Minmax计算是同步阻塞操作,会占用当前事件循环的线程,导致React无法及时处理玩家的状态更新和DOM渲染。直到Minmax计算完成、AI的setState执行后,React才会批量处理两次状态更新,最终表现为两者几乎同时显示,甚至AI的圆圈先出现。

使用setTimeout(fn, 0)的作用是将AI的计算和状态更新逻辑放到下一个事件循环中执行:当前事件循环会先处理玩家的setState更新,完成DOM渲染(显示叉号),然后再执行定时器中的AI逻辑,从而保证了玩家操作先被渲染,再执行AI的计算和显示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 10:18:39