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

React Props继承不更新问题:score在Gameboard/EndScene未同步

问题分析与解决方案

问题根源

你遇到的score props不更新问题,核心原因是手动使用ReactDOM.render渲染组件。Gameboard里的EndScene、按钮组、Character都是通过ReactDOM.render直接挂载到DOM节点的,这些组件脱离了React的组件树更新流程,不会响应App中score状态的变化。而Header正常是因为它在App的render函数里,属于React管理的组件树,会自动同步props更新。

另外,handleCharFound里直接修改原数组characterAlive.push(...),没有创建新数组,React无法检测到状态变化,会导致handleEndRound的判断逻辑出错。

修复步骤

1. 移除所有ReactDOM.render,改用React状态控制条件渲染

在Gameboard组件中添加状态currentView,用来控制当前显示的内容(初始界面、游戏界面、结束界面),完全通过React的组件渲染流程来管理,不再手动操作DOM渲染。

2. 修复状态更新方式

更新characterAlive时必须创建新数组,让React检测到状态变化,触发组件重新渲染。

3. 调整组件结构,让所有子组件都在Gameboard的return中渲染

把原来手动渲染的内容,改成条件渲染的形式,纳入React的组件树管理。

修改后的代码示例

Gameboard.js 修改版

import React, { useState, useEffect } from 'react'
import Character from './Character'
import EndScene from './EndScene'
import '../Styles/Gameboard.css'

export default function Gameboard(props) {
    const [charList, setCharList] = useState([])
    const [mapList, setMapList] = useState([])
    const [characterAlive, setCharacterAlive] = useState([])
    const [roundActive, setRoundActive] = useState(false)
    // 新增状态:控制当前显示的视图
    const [currentView, setCurrentView] = useState('initial') // initial | game | end
    // 存储当前游戏的地图和角色位置
    const [currentChars, setCurrentChars] = useState([])
    const [currentMap, setCurrentMap] = useState('')

    useEffect(() => {
        buildCharList()
        buildMapList()
    }, [])

    useEffect(() => {
        if (props.timer === 0 && roundActive) setCurrentView('end')
    }, [props.timer, roundActive])

    // 替换原来的handleEndScene,直接切换视图状态
    const handleEndScene = () => {
        setCurrentView('end')
    }

    const resetCharacterAlive = () => {
        setCharacterAlive([])
    }

    function handleEndRound() {
        if (characterAlive.length === charList.length) {
            props.setText('You Found All')
            props.increaseScore()
            setRoundActive(false)
            props.toggleTimer()
            setCurrentView('initial') // 回到初始界面
        }
    }

    function buildMapList() {
        function importAll(r) {
            return r.keys().map(r);
        }
        const images = importAll(require.context('../Assets/', true, /\.jpg/)) 
        setMapList(images);
    }

    function buildCharList() {
        function importAll(r) {
            return r.keys().map(r);
        }
        const images = importAll(require.context('../Assets/', true, /\.png/))
        setCharList(images);
    }

    function handleSetupGameboard() {
        const map = mapList.slice().sort(() => Math.random() - 0.5)[0]
        setCurrentMap(map)

        const gameboardRect = document.getElementById('gameboard').getBoundingClientRect()
        const charWidth = 70
        const charHeight = 70
        const maxX = gameboardRect.width - charWidth 
        const maxY = gameboardRect.height - charHeight

        const newCharList = charList.slice().sort(() => Math.random() - 0.5);
        // 生成带位置的角色数组,存储到状态中
        const charsWithPos = newCharList.map((char, index) => {
            const xPos = Math.floor(Math.random() * (maxX  + 1))   
            const yPos = Math.floor(Math.random() * (maxY  + 1))
            return {
                src: char,
                xPos,
                yPos: yPos - (index * charHeight),
                key: index
            }
        })
        setCurrentChars(charsWithPos)

        props.setText('Find Waldo, Lady Waldo and Wizard')
        setCurrentView('game') // 切换到游戏视图
    }

    function handleCharFound(src) {
        // 修复:创建新数组更新状态
        if (!characterAlive.includes(src)) {
            setCharacterAlive(prev => [...prev, src])
        }
        handleEndRound()
    }

    function handleButton() {
        resetCharacterAlive()
        setRoundActive(true)
        handleSetupGameboard()
        props.resetScore()
        props.resetTimer()
        props.toggleTimer()
    }

    return (
        <div className='gameBoardDiv'>
            <div 
                id='gameboard'
                style={{ backgroundImage: currentMap ? `url(${currentMap})` : '' }}
            >
                {/* 初始视图 */}
                {currentView === 'initial' && (
                    <>
                        <button onClick={handleButton} className='startGameBtn btn btn-info position-absolute'>Start Game</button>
                        <button onClick={handleEndScene} className='leaveGameBtn btn btn-danger position-absolute'>Leave Game</button>
                    </>
                )}

                {/* 游戏视图 */}
                {currentView === 'game' && (
                    currentChars.map(char => (
                        <Character
                            setText={props.setText}
                            src={char.src}
                            style={{
                                left: `${char.xPos}px`,
                                top: `${char.yPos}px`,
                            }}
                            key={char.key}
                            onClick={() => handleCharFound(char.src)}
                        />
                    ))
                )}

                {/* 结束视图 */}
                {currentView === 'end' && (
                    <EndScene 
                        handleButton={handleButton}
                        score={props.score}
                    />
                )}
            </div>
        </div>
    )
}

关键修改点说明

  • 新增currentView状态管理界面切换,完全抛弃ReactDOM.render
  • 角色和地图数据存储到状态中,通过条件渲染显示
  • handleCharFound中用setCharacterAlive(prev => [...prev, src])创建新数组,确保React能检测到状态变化
  • EndScene和游戏角色都在Gameboard的return中渲染,纳入React组件树,props会自动同步更新

验证效果

修改后,当App中的score状态更新时,Gameboard的props.score会同步更新,传递给EndScene的score也会正确显示。同时handleEndRound的逻辑也会正常触发,因为characterAlive的状态更新被React正确检测到。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 13:54:55