【发布时间】: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 是 <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
- ACTION 没有任何改变
- ACTION 没有任何改变
不确定这是否正常或导致我的问题。
如果您需要其他代码,请告诉我。
谢谢。
编辑:根据 cmets 中的建议更改了一些名称,问题仍然存在。
编辑2:问题似乎与useSelector直接相关:目前我有
gameState = useSelector(state => state.roomState)
但如果我选择整个状态,一切正常,组件重新渲染。
gameState = useSelector(state => state)
这远非理想:现在我需要在任何地方都调用gameState.roomState.something 而不是gameState.something。 useSelector怎么可能不识别特定部分状态的变化而只识别整个状态的变化?
【问题讨论】:
-
尝试将 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