React + Redux: компонент не перерисовывается при изменении состояния - PullRequest
0 голосов
/ 06 ноября 2019

Я делаю многопользовательскую игру на 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 devtools)
  • Похоже, что Lobby () просто не перерисовывается, несмотря на то, что useSelector работает,потому что каждый игрок видит список игроков, которые присоединились до него.

Я пытался искать видоизмененное состояние, но мне кажется, что там все хорошо. Это мой первый редукционный проект, так что, может быть, я что-то упустил. Одна подозрительная вещь, которую я заметил в redux devtools, заключается в том, что иногда (!) Последнее состояние распространяется через все предыдущие состояния в представлении devtools, например, вкладка «Diff» показывает, что все те же действия не привели к изменениям, за исключением того, что первое изменило состояние на конечную форму,например:

Фактические изменения:

  • ACTION изменил некоторое свойство на
  • ACTION изменил некоторое свойство на b
  • ACTION изменил некоторое свойство наc

Показанные изменения (но они стали такими только после третьего раза):

  • ACTION изменил какое-то свойство на a, какое-то свойство на b, а какое-то свойство на c
  • ДЕЙСТВИЕ ничего не изменило
  • Действие ничего не изменило

Не уверен, что это нормально или вызывает мою проблему.

Если вам нужен дополнительный код, дайте мне знать.

Спасибо.

РЕДАКТИРОВАТЬ: Изменены некоторые имена, согласно совету в комментариях, проблема по-прежнему остается.

РЕДАКТИРОВАТЬ 2: Проблема, кажется, напрямую связана с useSelector:в настоящее время у меня есть

gameState = useSelector(state => state.roomState)

но если я выбираю все состояние, все работает, компонент перерисовывается.

gameState = useSelector(state => state)

это далеко от идеала: теперь мне нужно вызвать gameState.roomState.something вместо gameState.somethingвезде. Как может быть, что useSelector не распознает изменение определенной части состояния и только распознает изменение всего состояния?

1 Ответ

0 голосов
/ 07 ноября 2019

После вашего второго редактирования я лучше посмотрел на ваше действие, чтобы выяснить, почему state.roomState не изменился для триггера useSelector, и не могу поверить, тот же вопрос, на который я ответил две недели назад - Как мне отправить несколько действий в реагирующем с использованием хуков?

вы можете прикрепить более одной полезной нагрузки к отправке. Измените действие 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 }
...