为什么状态不更新

Why does the state not update

当新用户访问页面时,他们可以填写用户名,该用户名存储在 state 和 cookie 中。我稍后想访问该 cookie 和状态以在游戏 session 中识别用户名,但此时它们都是空的,即使我知道它们已被设置(日志记录看到值,用户名正确显示在header)。为什么会这样?

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) 在页面的 header 中看到了从 cookie 加载的用户名。

那为什么这里显示为undefined呢? useState 也是如此,它显示初始值 (null),而不是实际值...

编辑:完整代码在这里 - https://github.com/faire2/loreHunters/blob/master/src/components/loginPage/LoginPage.js

代码在 arnak-dev.herokuapp.com.

上线

问题

  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
    });
    
  2. 从错误的渲染周期访问状态

    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);
    }
    

建议

我认为这些更改应该能让您的代码变得更好,但可能需要对依赖项和条件测试进行一些调整。

  1. 通过在 效果之外声明回调来修复陈旧的外壳。这里的想法是 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);
    }, []);
    
  2. 通过将更新的状态变量作为依赖项触发的单独效果中的“反应”状态更新来修复状态访问。

    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]);