Coroutine as dependency to useEffect

Viewed 108

Current I have the following custom hook:

function useSecurity() {
  const { getAccessTokenSilently } = useAuth0()

  const getAccessToken = async () => {
    return await getAccessTokenSilently()
  }
  
  const getAuthHeader = async () => {
    return { Authorization: `Bearer ${await getAccessToken()}` }
  }
   
  return { getAuthHeader }
}

In a page where I use it I have:

const { getAuthHeader } = useSecurity()

useEffect(() => {
  async function fetchData() {
    ...
    }
  }
  fetchData()
}, [getAuthHeader])

This code causes infinite loop page load, I assume because getAuthHeader is a coroutine. I know I can don't have to include it in the deps (and ignore eslint warnings), but is there a way I can include it in the deps and not trigger the infinite loop?

2 Answers

The problem with your code is that getAuthHeader is used like a hook, but it's actually async method. You can try setting it up like this:

function useSecurity() {
  const [token, setToken] = useState('');
  const { getAccessTokenSilently } = useAuth0();

  useEffect(() => {
    getToken = async () => {
      const token = await getAccessTokenSilently();
      setToken(token)
    }

    getToken();
  }, []);
   
  return { token }
}

This was you'd have the token stored in state of useSecurity hook and then just use it like this:

const { token } = useSecurity()

useEffect(() => {
  async function fetchData() {
    ...
    }
  }
  fetchData()
}, [token])

Using david's suggestion to use useCallback() fixes the issue. The working code is:

function useSecurity() {
  const { getAccessTokenSilently } = useAuth0()

  const getAccessToken = async () => {
    return await getAccessTokenSilently()
  }
  
  const getAuthHeader = async () => {
    return { Authorization: `Bearer ${await getAccessToken()}` }
  }
   
  return { getAuthHeader: useCallback(getAuthHeader, []) }
}
Related