新用户访问页面时,他们可以填写用户名,该用户名存储在状态和cookie中。稍后,我想访问该cookie并声明在游戏会话中的用户名,但是即使我知道它们已被设置(此时日志中会看到这些值,用户名也正确显示在标题中),此时它们都为空。为什么会这样?
const [cookies, setCookie] = useCookies(["username"]);
const [formerUsername, setFormerUsername] = useState(cookies.username ? cookies.username : null);
...
function handleUsernameChange(username) {
setCookie("username", username, {path: "/", expires: expirationDate});
setFormerUsername(username);
...
}
useEffect(() => {
...
socket.on(TRANSMISSIONS.startGame, data => {
let playerIndex = data.room.players.indexOf(cookies.username);
history.push({pathname: "/game", data: {username: cookies.username, room: data.room, playerIndex: playerIndex,
}});
});
播放器索引为-1,因为未定义cookies.username。我知道它已设置,因为我a)看了cookie,b)看了页面标题中从cookie加载的用户名。
那么为什么它在这里显示为undefined? useState也是如此,它显示初始值(null
),而不是实际值...
编辑:完整的代码在这里-https://github.com/faire2/loreHunters/blob/master/src/components/loginPage/LoginPage.js
该代码位于arnak-dev.herokuapp.com。
答案 0 :(得分:1)
回调中的陈旧附件以及放错效果的依赖项数组
useEffect(() => {
// extend cookie if it exists
...
socket.on(TRANSMISSIONS.startGame, data => {
let playerIndex = data.room.players.indexOf(cookies.username);
history.push({
pathname: "/game",
data: {
username: cookies.username, // <-- value when effect ran
room: data.room,
playerIndex: playerIndex,
},
});
});
...
socket.on(TRANSMISSIONS.currentUsersAndData, data => {
console.log("received actual room and users data");
if (!shakedHand) { // <-- stale
setShakedHand(true)
}
setUsers(data.users);
setRooms(data.rooms);
setRoomIsFull(false);
}, []) // <-- dependency array
});
从错误的渲染周期访问状态
function handleUsernameChange(username) {
setCookie("username", username, {path: "/", expires: expirationDate});
setFormerUsername(username);
if (!formerUsername) { // <-- current formerUsername state
socket.emit(
TRANSMISSIONS.handShake,
cookies.username, // <-- current cookies state
);
} else {
socket.emit(
TRANSMISSIONS.usernameChanged,
{
formerUsername: formerUsername, // <-- current formerUsername state
newUsername: username, // <-- current username state
},
);
}
setShowCreateUsername(false);
}
我认为这些更改应该使您的代码更好,但是可能需要对依赖项和条件测试进行一些调整。
通过声明回调 outside 的效果来修复陈旧的附件。这里的想法是onStartGameHandler
在每个渲染周期中定义,并包含当前状态。
const onStartGameHandler = data => {
const playerIndex = data.room.players.indexOf(cookies.username);
history.push({
pathname: "/game",
data: {
username: cookies.username,
room: data.room,
playerIndex: playerIndex,
},
});
};
const onUsersAndDataHandler = data => {
console.log("received actual room and users data");
if (!shakedHand) {
setShakedHand(true)
}
setUsers(data.users);
setRooms(data.rooms);
setRoomIsFull(false);
}
useEffect(() => {
// extend cookie if it exists
if (cookies.username && !shakedHand) { ...
...
socket.on(TRANSMISSIONS.startGame, onStartGameHandler);
...
socket.on(TRANSMISSIONS.currentUsersAndData, onUsersAndDataHandler);
// return clean up function
return () => socket.off(TRANSMISSIONS.startGame);
}, []);
通过“反应”状态更新来修复状态访问,该状态更新由被更新的状态变量作为依存关系触发的 触发的单独效果。
function handleUsernameChange(username) {
setCookie("username", username, {path: "/", expires: expirationDate});
setFormerUsername(username);
setShowCreateUsername(false);
}
...
useEffect(() => {
if (!formerUsername) {
socket.emit(TRANSMISSIONS.handShake, cookies.username);
} else {
socket.emit(
TRANSMISSIONS.usernameChanged,
{
formerUsername: formerUsername,
newUsername: username,
},
);
}
}, [cookies, formerUsername, username]);