【问题标题】:React + Redux: component not rerendering on state changeReact + Redux:组件不会在状态更改时重新渲染
【发布时间】:2020-03-03 04:50:06
【问题描述】:

我正在 React 上制作多人游戏,并使用 Colyseus.js 作为我的多人 API。问题是我需要在大厅中显示所有连接的玩家:

export default function Lobby() {
    const classes = useStyles();
    const gameState = useSelector(state => state.roomState);
    const history = useHistory();

    // Creating array to display all connected players
    let playersArray = [];
    if(Object.entries(gameState).length !== 0) {
        playersArray = Object.values(gameState.players.toJSON());

    }
    // Check if game has started
    useEffect(()=>{
        console.log('gamestarted:', gameState.gameStarted);
        if(gameState.gameStarted){
            history.push('/game')
        }
    }, [gameState.gameStarted]);
    return (
        <PageFrame>
            <Grid
                container
                direction="column"
                alignItems="center"
                className={classes.root}
                spacing={2}
            >
                <Grid item xs={12}>
                    <Typography className={classes.gameTitle}>
                        {(gameState != null) ? gameState.gameName : <CircularProgress />}
                    </Typography>
                </Grid>
                <Grid item xs={12}>
                    Waiting for all players to join...
                </Grid>
                <Grid item xs={12}>
                    Connected Players: {playersArray.length}
                </Grid>
                <Grid item xs={12}>
                    {playersArray.map((data, index) => {
                        return (
                            <Chip
                                key={index}
                                label={data.party}
                                className={classes.chip}
                                style={{"--color": green[500]}}
                            />
                        );
                    })}
                </Grid>
                <Grid item xs={12}>
                </Grid>
            </Grid>
        </PageFrame>
    );
}

我使用useSelector 来获取我从 Colyseus.js 收到的状态并像这样发送(在另一个组件中,Lobby&lt;Route&gt; 的子组件):

const joinRoom = (id, partyName, actions) => {
        client.joinById(id, {partyName, id}).then(room => {
            dispatch({type: GAME_ROOM, payload: room });
            dispatch({type: RECONNECT_PARAMS, payload: {roomId: room.id, sessionId: room.sessionId} });
            // Redirect to lobby
            history.push('/lobby')
            {...}
            room.onStateChange((state) => {
                console.log("the room state has been updated:", state);
                // HERE DISPATCHING RECEIVED STATE
                dispatch({type: GAME_STATE, payload: state });
            });
            room.onLeave((code) => {
                {...}
        });
    }

来自服务器的state 具有带有对象的属性players,该对象具有带有我需要显示的玩家姓名的键。 我的减速机:

const initialState = {
    joinDetails:{
        gameCode: '',
        partyName: ''
    },
    sessionParams: {
        roomId: "",
        sessionId: "",
    },
    room: {},
    roomState: {},
    scoreChange: null,
    totalChange: null,
    playerState:{
        showResults: false,
        questions: [],
        totalBudget: 5000,
        totalGlobalBudget: 5000,
        budget: [],
        activeStep: 0,
        stepCompleted: [false,false,false,false,false],
        decisions: [-1,-1,-1,-1],

    }

};

function rootReducer(state = initialState, action) {

    switch (action.type) {
        case JOIN_FORM:
            return {...state, joinDetails: action.payload};
        case GAME_ROOM:
            return {...state, room: action.payload};
        case GAME_STATE:
            return {...state, roomState: action.payload};
        case RECONNECT_PARAMS:
            return {...state, sessionParams: action.payload};
        case CHANGE_STEP:
            if(action.payload === 'add'){
                return {...state, playerState: {...state.playerState, activeStep: state.playerState.activeStep + 1}}}
            else{
                return {...state, playerState: {...state.playerState, activeStep: state.playerState.activeStep - 1}}}
        case COMPLETE_STEP:
            return {...state, playerState: {...state.playerState, stepCompleted: action.payload}};
        case SET_BUDGET:
            return {...state, playerState: {...state.playerState, budget: action.payload}};
        case SET_QUESTIONS:
            return {...state, playerState: {...state.playerState, questions: action.payload}};
        case SET_TOTAL_BUDGET:
            if(action.payload.add !== undefined){
                return {...state, playerState: {...state.playerState, totalBudget: state.playerState.totalBudget + action.payload.add}}
            }
            else{
                return {...state, playerState: {...state.playerState, totalBudget: action.payload}};
            }

        case SET_GLOBAL_BUDGET:
            if(action.payload.add !== undefined){
                return {...state, playerState: {...state.playerState, totalGlobalBudget: state.playerState.totalGlobalBudget + action.payload.add}}
            }
            else{
                return {...state, playerState: {...state.playerState, totalGlobalBudget: action.payload}}
            }
        case SET_DECISIONS:
            return {...state, playerState: {...state.playerState, decisions: action.payload}};
        case SET_CHANGES:
            return {...state, scoreChange: action.payload.change, totalChange: action.payload.total};
        case SHOW_RESULTS:
            return {...state, playerState: {...state.playerState, showResults: action.payload}};
        case CLEAR_STATE:
            return {...state, playerState: {}};

        default:
            return state;
    }
}
export default rootReducer;

问题是新玩家加入后玩家列表没有更新。我可以确认:

  • 我从服务器收到新玩家的状态
  • 我成功地将这个状态分派给 redux(我通过 redux devtools 看到)
  • 看起来 Lobby() 根本没有重新渲染,尽管 useSelector 有效,因为每个玩家都会看到在他之前加入的玩家列表。

我尝试寻找变异的状态,但在我看来我一切都很好。这是我的第一个 redux 项目,所以也许我遗漏了一些东西。 我在 redux devtools 中注意到一件可疑的事情,有时(!)最新状态会传播到 devtools 视图中的所有先前状态,例如“差异”选项卡显示除了第一个已将状态更改为最终形式外,所有相同的操作均未导致任何更改,例如:

实际变化:

  • ACTION 将某些属性更改为
  • ACTION 将某些属性更改为 b
  • ACTION 将某些属性更改为 c

显示的变化(但在第三次之后才变成这样):

  • ACTION 将某些属性更改为 a,将某些属性更改为 b,还有一些 属性给c
  • ACTION 没有任何改变
  • ACTION 没有任何改变

不确定这是否正常或导致我的问题。

如果您需要其他代码,请告诉我。

谢谢。

编辑:根据 cmets 中的建议更改了一些名称,问题仍然存在。

编辑2:问题似乎与useSelector直接相关:目前我有

gameState = useSelector(state => state.roomState)

但如果我选择整个状态,一切正常,组件重新渲染。

gameState = useSelector(state => state)

这远非理想:现在我需要在任何地方都调用gameState.roomState.something 而不是gameState.somethinguseSelector怎么可能不识别特定部分状态的变化而只识别整个状态的变化?

【问题讨论】:

  • 尝试将 useSelector 改为 - { const gameState = useSelector(state => state);或从您的初始状态到 state.joinDetalis 等
  • @HagaiHarari 你的意思是只返回整个状态吗?现在我从状态返回“状态”键,也许是命名导致了问题?
  • 好的,所以我更好地查看了您的代码,并在您的 intialState“状态”方法中看到了 state.state 所指的方法。但可能将其称为“myState”,因为例如这里 { {...state, "joinDetails": action.payload} } joinDetails 假设没有 " "
  • @HagaiHarari 我更改了命名,删除了引号,但它仍然不起作用:状态设置,但大厅没有重新渲染......所以我的代码中没有什么明显错误的了吗?跨度>
  • 如果我理解正确,除了如果有用户 a 和用户 b 加入,用户 b 看到用户 a 但看不到另一边,所有工作都按预期工作,对吗?如果是这样,您是否考虑过将 Socket.io 添加到项目中?

标签: javascript reactjs react-redux react-hooks


【解决方案1】:

所以我的问题就在这里:

room.onStateChange((state) => {
                console.log("the room state has been updated:", state);
                // HERE DISPATCHING RECEIVED STATE
                dispatch({type: GAME_STATE, payload: state });

我将state 放入有效负载中,而没有将其解构/复制到新对象,因此实际上只传递了一个引用。当然,当它改变时,它也改变了所有其他参考,没有什么可比较的。正确的方法是将收到的state 解构,同时将其传递给操作:

room.onStateChange((state) => {
                console.log("the room state has been updated:", state);
                // HERE DISPATCHING RECEIVED STATE
                dispatch({type: GAME_STATE, payload: {...state} })

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    相关资源
    最近更新 更多