Home > Mobile >  How can I call a callback that depends on an updated state in react?
How can I call a callback that depends on an updated state in react?

Time:12-30

function useRequest() {

  const [accessToken, setAccessToken] = useState(42);
  const [refreshToken, setRefreshToken] = useState(7);
  
  const request = async (url) => {
    try {
      const res = await axios.get(url, { Headers: { Auth: accessToken } });
    } catch (error) {
      if (error.response.status === 401) {
        refresh(() => request(url));
      }
    }
  }
  
  const refresh = async (callback) => {
    const res = await axios.get("myserver.com", { params: { refreshToken }});
    setAccessToken(res.data.accessToken);
    callback()
  }
  
  return request;

}

So I want to call this api and refresh the access token if it expires. Problem is, when I set the access token state in the refresh function, the callback is called with the old state because setState is async. In other contexts, I could just use useEffect but i'm not sure what to do in this case.

CodePudding user response:

If you don't want to use useEffect for whatever reason, another option would be to pass the state you set to the request function, so that it doesn't depend on external variables.

const request = async (url, accessToken = 42) => {
    try {
        const res = await axios.get(url, { Headers: { Auth: accessToken } });
    } catch (error) {
        if (error.response.status === 401) {
            refresh((accessToken) => request(url, accessToken));
        }
    }
}

const refresh = async (callback) => {
    const res = await axios.get("myserver.com");
    const { accessToken } = res.data;
    setAccessToken(accessToken);
    callback(accessToken);
}

CodePudding user response:

I think you want to store accessToken in a ref instead of in state.

Official react documentation: https://reactjs.org/docs/hooks-reference.html#useref

useState is async because React assumes state is related to the UI, so state updates are coupled to the React render lifecycle.

Updating the ref is synchronous, and is a good option for persisting data that doesn't need to trigger UI updates.

Your code, but using useRef

function useRequest() {

  const accessTokenRef = useRef(42);
  const [refreshToken, setRefreshToken] = useState(7);
  
  const request = async (url) => {
    try {
      const res = await axios.get(url, { Headers: { Auth: accessTokenRef.current } });
    } catch (error) {
      if (error.response.status === 401) {
        refresh(() => request(url));
      }
    }
  }
  
  const refresh = async (callback) => {
    const res = await axios.get("myserver.com", { params: { refreshToken }});
    accessTokenRef.current = res.data.accessToken;
    callback()
  }
  
  return request;

}
  • Related