useEffect 钩子中的 useState set 方法直到刷新页面才更新

useState set method inside useEffect hook not updating till refreching the page

我正在尝试在 token 更改时更新 user。我有两个钩子,它们是 useToken(在登录和注销事件时处理令牌)和 useUser(在令牌值发生任何变化后更新用户) .我在 HeaderHelper 中使用了它们,它是一个功能组件 returns 一个 Header Component 接受 user, token, setToken 和 navigate as props. 在后者中,我编写了一个名为 afterLogin 的函数来调用 setToken 以更新token 字段并调用导航挂钩以重定向到适当的路由。

问题是登录后,虽然调用了setToken和useEffect,但用户字段没有得到任何值并保持为“null”。为了达到我想要的结果,我应该刷新浏览器,这显然不实用。

quote NOTE: useUser has a token field as a part of its state. and the HeaderHelper has a user and token as a part of its state.

这里是useToken Hook的代码:

export const useToken = () => {
    const [ token, setToken ] = useState(()=>{
        return sessionStorage.getItem('token');
    });

    function setTokenStorage (newToken) {
        sessionStorage.setItem('token', newToken);
        setToken(newToken);
    }

    return [token, setTokenStorage ];
}

这里是useUser Hook的代码:

export const useUser = () =>  {
    const [ token ] = useToken();
    
    const getPayloadFromToken = token => {
        console.log(token);
        const encodedPayload = token.split('.')[1];
        console.log(JSON.parse(atob(encodedPayload)), ': encodedPayload');
        return JSON.parse(atob(encodedPayload));
    }

    const [ user, setUser] = useState(()=>{
        // console.log('token from user useState', token , '***');
        if(!token) {
            console.log(token);
            return {};
        };
        // console.log(getPayloadFromToken(token));
        return getPayloadFromToken(token);
    });

    useEffect( ()=>{
        console.log('token from user useEffect', token , '***');
        if(!token) {
            console.log('user will become null');
            setUser({});
        }else{
            console.log(' useEffect ');
            setUser(getPayloadFromToken(token));
        }
    },[token]);

    return  [user];
}

HeaderHelper组件代码如下:

export const HeaderHelper = (props) => {
    const [user]  = useUser();
    const navigate = useNavigate();
    const [token, setToken] = useToken();
    return <Header navigate={ navigate } token={token} setToken={ setToken } user={ user } {...props}/>
}

Header 组件的代码如下:

class Header extends Component{
    constructor (props){
        super(props);
        this.state = { ... }
    }
    handleLogin(e){
        e.preventDefault();
        this.setState({successSingIn : false});
        fetch('http://localhost:8000/api/login', {
            method: 'POST',
            body: JSON.stringify({
                "email" : this.email.value,
                "password" : this.password.value,
                "type" : this.type.value,
            }),
            headers: { "Content-Type" : "application/json"},
            credentials: "same-origin"
        }).then(response => {
            if (response.ok){
                return response;
            }else{
                const error = new Error('Error ' + response.status + ' : ' + response.statusText);
                error.response = response;
                throw error;
            }
        }).then(response => response.json())
        .then(response => {
            console.log('response from backend: ' + JSON.stringify(response));
            if (response.status) {
                this.setState({ failToLogIn : '' });
                this.afterLogin(response.user.token);
            }else{
                this.setState({ failToLogIn : 'something wrong, try again with valid information!' });
            }
        })
        .catch(error => {
            this.setState({ failToLogIn : 'something wrong' });
            console.log('something wrong with login process\n' , error.message)
        });
    }
}

这里是 afterLogin 函数的代码:

afterLogin = (userToken) => {
        this.toggleLoginModel();
        console.log('userToken: ', userToken);
        this.props.setToken(userToken);
        this.props.navigate('/dashboard');
        
    }

总结: -我是这样想的-
-1 来宾发出登录请求。
-2 登录成功后调用的 afterLogin 函数。
-3 然后,调用 setToken,导致在 useEffect 中调用该函数。并重新呈现 HeaderHelper。
-4 最后,将调用 setUser 并重新呈现 HeaderHelper。
-5 毕竟令牌和用户应该有值,不幸的是那没有发生。
那么我该如何解决这个问题呢?

谢谢!

是的,看起来每个 useToken 挂钩都保持着自己的状态。 setToken Header 使用的是 useToken 而没有更新 useUsers 使用的 useToken 钩子的 token 状态,所以useEffect 挂钩不会重新触发。

您可能会尝试从 useUser 使用的 useToken 挂钩中 return tokensetToken,因此只有一个实例.

export const useUser = () =>  {
  const [token, setToken] = useToken();
    
  const getPayloadFromToken = token => {
    console.log(token);
    const encodedPayload = token.split('.')[1];
    console.log(JSON.parse(atob(encodedPayload)), ': encodedPayload');
    return JSON.parse(atob(encodedPayload));
  }

  const [user, setUser] = useState(() => {
    // console.log('token from user useState', token , '***');
    if (!token) {
      console.log(token);
      return {};
    };
    // console.log(getPayloadFromToken(token));
    return getPayloadFromToken(token);
  });

  useEffect( ()=>{
    console.log('token from user useEffect', token , '***');
    if (!token) {
      console.log('user will become null');
      setUser({});
    } else {
      console.log(' useEffect ');
      setUser(getPayloadFromToken(token));
    }
  }, [token]);

  return [user, token, setToken];
}

...

export const HeaderHelper = (props) => {
  const [user, token, setToken] = useUser();
  const navigate = useNavigate();
  return (
    <Header
      {...props}
      navigate={navigate}
      token={token}
      setToken={setToken}
      user={user}
    />
  );
}