ryanandrew2233
ryanandrew2233

Reputation: 63

setState not updating state on initial submit, only on subsequent submits

I'm trying to use session storage to set some data, It doesn't work the first time, but works the second time.

  const login = async (email, password) => {
    try {
        const user = await loginUser({
          variables: {
            email,
            password
          }
        });

        const { userId, token, expiresIn } = user.data.loginUser;

        setUserData({
          token: token,
          userId: userId,
          expiresIn: expiresIn
        })

        sessionStorage.setItem('interdevs-data', JSON.stringify({ 
          "token": userData.token, 
          "userId": userData.userId, 
          "expiresIn": userData.expiresIn 
        }));
    } catch(err) {
      setLoginErr(err);
    };
  };


I'm expecting it to setState on initial submit, but it's okay setting it when I click submit again

Upvotes: 0

Views: 43

Answers (2)

thortsx
thortsx

Reputation: 2280

useState in React Hook also is asynchronous like this.setState in React Component.

So, you can try:

const login = async (email, password) => {
    try {
        const user = await loginUser({
          variables: {
            email,
            password
          }
        });

        const { userId, token, expiresIn } = user.data.loginUser;

        // setUserData({
        //   token: token,
        //   userId: userId,
        //   expiresIn: expiresIn
        // })
        // userData here not yet updated
        sessionStorage.setItem('interdevs-data', JSON.stringify({ 
          "token": token, 
          "userId": userId, 
          "expiresIn": expiresIn 
        }));
        // In your case, you can direct access to user.data.loginUser. Don't need use setUserData
    } catch(err) {
      setLoginErr(err);
    };
  };

Or if you still want use setUserData, try apply useEffect like:

const login = async (email, password) => {
    try {
        const user = await loginUser({
          variables: {
            email,
            password
          }
        });
        const { userId, token, expiresIn } = user.data.loginUser;
        setUserData({
           token: token,
           userId: userId,
           expiresIn: expiresIn
        })
    } catch(err) {
      setLoginErr(err);
    };
  };

useEffect(() => {
    sessionStorage.setItem('interdevs-data', JSON.stringify({ 
          "token": userData.token, 
          "userId": userData.userId, 
          "expiresIn": userData.expiresIn 
        }));
}, [userData])

Upvotes: 0

Cat_Enthusiast
Cat_Enthusiast

Reputation: 15688

The setUserData() method is asynchronous, which means there's nothing stopping the code written after it from executing before its own logic has completed. This is true for all state-updating methods.

What's likely happening is that the sessionStorage.setItem() block runs before you've completely updated the state in the previous block. So there's nothing to save in localStorage the first-time around. But on subsequent attempts the state is updated with the token/user-info from the previous update.

There's a couple ways of resolving this:

1) Is to just update the local-storage with the variables you created instead of waiting for state to finish:

  const login = async (email, password) => {
    try {
        const user = await loginUser({
          variables: {
            email,
            password
          }
        });

        const { userId, token, expiresIn } = user.data.loginUser;

        setUserData({
          token: token,
          userId: userId,
          expiresIn: expiresIn
        })

        sessionStorage.setItem('interdevs-data', JSON.stringify({ 
          "token": token, 
          "userId": userId, 
          "expiresIn": expiresIn 
        }));
    } catch(err) {
      setLoginErr(err);
    };
  };

2) Employ useEffect() to update localStorage when state-changes.

  useEffect(() => {
        sessionStorage.setItem('interdevs-data', JSON.stringify({ 
          "token": userData.token, 
          "userId": userData.userId, 
          "expiresIn": userData.expiresIn 
        }));
  }, [user]) //swap user with whatever you called your state

  const login = async (email, password) => {
    try {
        const user = await loginUser({
          variables: {
            email,
            password
          }
        });

        const { userId, token, expiresIn } = user.data.loginUser;

        setUserData({
          token: token,
          userId: userId,
          expiresIn: expiresIn
        })

    } catch(err) {
      setLoginErr(err);
    };
  };

Upvotes: 1

Related Questions