React + Redux:组件不会在状态更改时重新呈现

时间:2019-11-06 16:29:10

标签: javascript reactjs react-redux react-hooks

我正在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中以<Route>作为子级的另一个组件中):

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的属性
  • 动作什么都没改变
  • 动作什么都没改变

不确定这是正常现象还是导致我的问题。

如果您需要附加代码,请告诉我。

谢谢。

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

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

gameState = useSelector(state => state.roomState)

但是,如果我选择整个状态,则一切正常,组件会重新显示。

gameState = useSelector(state => state)

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

2 个答案:

答案 0 :(得分: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} })

答案 1 :(得分:0)

第二次编辑后,我更好地了解了您的操作,以找出为什么state.roomState并未更改以触发useSelector,并且不敢相信,这与我两周前回答的问题完全相同-{{ 3}}

您可以附加多个有效载荷来进行调度。将joinRoom操作更改为一个调度,同时保留“ GAME_ROOM”和“ RECONNECT_PARAMS”


 client.joinById(id, {partyName, id}).then(room => {
        dispatch({
              type: 'GAME_ROOM',
              room,
              params: {roomId: room.id, sessionId: room.sessionId} 
     });
  ..........

然后在化简器中将它们组合为一个案例侦听器


 case 'GAME_ROOM':
        return {...state, room: action.room, sessionParams: action.params }