#strange behavior on token

13 messages · Page 1 of 1 (latest)

void tide
#

I have a token, that I put in localStorage, then I have this below in a global context, in authProvider

const [accessToken, setAccessToken] = useState<string | null>(
    localStorage.getItem(ACCESS_TOKEN) ?? null,
  );

Then after login, if people check remember me, we put that in localStorage, otherwise we just setAccessToken(token) so it's stored in global state, so we can have

const {accessToken} = useAuth()

And in this component I have console.log and see that we definitely have the token, it's not null or undefined. However, in my useQuery it will result in some data fetching error.

But if I wrote something extra such as

 const token = localStorage.getItem(ACCESS_TOKEN)?? accessToken;

then use this token, instead of accessToken directly from useAuth, I did not have errors.

Component B:

 const {accessToken} = useAuth() as AuthContextType;

// have to add a line below to make it work but it doesn't make sense to me if I already do it in useAuth
  const token = localStorage.getItem(ACCESS_TOKEN) ?? accessToken;

// fetch data
  const {isLoading: isLoadingEmail} = useQuery(
    ['emailData', token],
    async () => {
      return await axiosClient.get(ACCOUNT_EMAIL_URL, {
        params: {
          token,
        },
      });
    },
    {
      onSuccess: (res) => {
        setEmail(res.data.email);
      },
      onError: (err) => {
        console.error(err);
        setEmailErr('error loading data');
      },
    },
  );

Why would this happen? Isn't this repeated as I already checked the local storage one in the useAuth?

#

@frozen bone Since we just talked about component A and component B in another thread. This strange behavior only happens in component B, but not in component A. Or component C which doesn't use the same queryKey anywhere else.... I am just wondering why?

The error was actually why I was thinking about ditching the useQuery in component B and use that reading from queryClient... but that doesn't seem to be a good strategy. Now I somehow fixed the error by that extra line, but it doesn't make sense to me why this would fix it.

frozen bone
#

And in this component I have console.log and see that we definitely have the token, it's not null or undefined. However, in my useQuery it will result in some data fetching error.

Can you share the code including the usage of useQuery and the console.log, and also provide details about the data fetching error?

void tide
#

unfortunately that backend part I can't share.. but I can write a bit in the above. The error was 401, meaning unauthorized, which means somehow the token might not be attached.

#

I wrote a simplified component B. The part that doesn't make sense to me is that additional check for token. I want to just use that accessToken I got from useAuth, since I already did that localstorage check there.

frozen bone
#

When is the authToken stored in localStorage? If it is stored after useState<string | null>(localStorage.getItem(ACCESS_TOKEN) ?? null); is called, the state will also need to be refreshed by calling setAccessToken.

void tide
#

After login, we get a refresh token, we use that to exchange access token. If use chooses (keep me logged in), then both are stored in local storage, if not checked, nothing is stored in local storage.

#

so... when not checked, basically we are not check local storage and totally rely on our useAuth for tokens ... this is where it got me confused.

frozen bone
#

I would try this:

const [accessToken, setAccessToken] = useState<string | null>(()=>{
    const value = localStorage.getItem(ACCESS_TOKEN) ?? null;
    console.log('Storing token to state', value);
    return value;
  });

If it logs Storing token to state: null that means the token is not yet in localstorage at the time the state is initialized. If that's the case it'll remain null until it is updated with setAccessToken.

void tide
#

great tip, I will give it a debug

void tide
#

Ok, after poking around, I decided to add extra check, such as

// fetch data
  const {isLoading: isLoadingEmail} = useQuery(
    ['emailData', token],
    async () => {
      if (token){
      return await axiosClient.get(ACCOUNT_EMAIL_URL, {
        params: {
          token,
        },
      });
     }
    },
    {
      onSuccess: (res) => {
        setEmail(res.data.email);
      },
      onError: (err) => {
        console.error(err);
        setEmailErr('error loading data');
      },
    },
  );

In that way, only when token or other things needed are fetched, we do that useQuery call. Otherwise it will call as soon as possible and result in some error when network is delayed.

frozen bone
#

If you want the query to be disabled when the token is null, you can also use the enabled option: enabled: !!token.

void tide
#

oh nice to know!