Reactjs 为什么状态不更新

Reactjs 为什么状态不更新,reactjs,cookies,use-effect,use-state,Reactjs,Cookies,Use Effect,Use State,当新用户访问页面时,他们可以填写用户名,该用户名存储在state和cookie中。稍后,我想访问cookie和state来标识游戏会话中的用户名,但是在那一点上它们都是空的,即使我知道它们已经设置好了(日志记录可以看到值,用户名正确地显示在标题中)。为什么会这样 const [cookies, setCookie] = useCookies(["username"]); const [formerUsername, setFormerUsername] = useState(

当新用户访问页面时,他们可以填写用户名,该用户名存储在state和cookie中。稍后,我想访问cookie和state来标识游戏会话中的用户名,但是在那一点上它们都是空的,即使我知道它们已经设置好了(日志记录可以看到值,用户名正确地显示在标题中)。为什么会这样

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加载的用户名

那么为什么在这里显示为未定义?useState也是如此,它显示初始值(
null
),而不是实际值

编辑:完整代码在此处-

代码在arnak-dev.herokuapp.com上直播

问题
  • 回调中的过时存储模块和错位效应依赖项数组

    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);
      setShowCreateUsername(false);
    }
    
    ...
    
    useEffect(() => {
      if (!formerUsername) {
        socket.emit(TRANSMISSIONS.handShake, cookies.username);
      } else {
        socket.emit(
          TRANSMISSIONS.usernameChanged,
          {
            formerUsername: formerUsername,
            newUsername: username,
          },
        );
      }
    }, [cookies, formerUsername, username]);
    

  • “当一个新用户访问页面时,他可以填写用户名”-如果你的访问者是女性怎么办?如果你使用一个特效来简单地记录对
    cookies
    的更改,你会在你的组件中看到它的更新吗?您能提供一个更完整的组件示例吗?
    socket
    的作用是什么?我怀疑您已经关闭了
    startName
    回调中的
    cookies
    值。
    cookies.username
    useState(cookies.username?cookies.username:null)时尚未定义,并且您的
    useffect
    仅在安装组件时运行一次。当
    cookies
    状态更新时,您无法更新任何套接字详细信息。对,我认为您有一些问题。首先,是的,由于
    useffect
    有一个空的依赖项数组,您似乎已经关闭了
    socket
    事件处理程序中的初始
    cookies
    状态。其次,在一些函数中调用
    setCookie
    ,但随后一两行会发出当前的
    cookie.username
    值(而不是刚刚排队等待下一个渲染周期的值)。第三,(我猜与第一个相关)您的效果不会返回清理功能(a)断开/关闭任何打开的套接字,以及(B)重新关闭事件处理程序中更新的
    cookies
    状态值。套接字需要保持打开状态,直到您导航到新路由,或者至少,注销登录页组件添加的事件处理程序。这可能是您想要的带有返回清理函数的挂载效果。我认为要解决
    cookies
    值的主要问题,需要定义依赖于effect hook外部的
    cookies
    的套接字回调。如果您愿意,我可以提供一个示例作为答案。我已经实现了您提供的两个更改,但仍然发生相同的错误。我已将您的更改推送到回购协议中,以便您可以查看它们,您还可以查看现场的情况。我更不了解的是,
    if(cookies.username&&!shakedHand)
    useEffect的一部分立即注册新的cookie。我真是太傻了。@Faire我在heroku上玩过你的应用程序,它在游戏开始前似乎一直有效,但我在黑暗中跌跌撞撞。我看到
    formerUsername
    状态更新到我的用户名、cookies集。我想我看到
    shaked和
    状态没有更新。你能提供复制步骤吗?复制是100%:1。不存在cookie。2.创建新用户名。3.不刷新创建一个新游戏并启动它。它将崩溃,因为它将无法确定用户。但似乎传输总是触发页面初始化时创建的useState效果。我更改了代码,以便通过日志显式地将用户名传递给startGame函数。我看到用户名被正确地传递到函数(调试器显示useEffect知道新用户名),但是一旦它到达StartName函数,用户名就会再次被取消定义。日志显示StartName函数实际上运行了两次-首先使用未定义的用户名,然后(太晚)使用已定义的用户名。
    
    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]);