React - useEffect with async setState causing additional re-renders

Viewed 308

I have a React hook here

export const useConfigDetails = (resourceName: string) => {
  const [isLoading, setLoading] = React.useState<boolean>(true);
  const [error, setError] = React.useState<AxiosError>(undefined);
  const [allEnvDetails, setAllEnvDetails] = React.useState<ResourceDetails[]>();


  React.useEffect(() => {
   const configPromises = ['dev', 'stage', 'prod'].map((environment: Environment) =>
    axios.get('someUrlGetEndpoint'));

    Promise.all(configPromises)
      .then((resp: ResourceDetails[]) => setAllEnvDetails(resp))
      .catch((err: AxiosError) => setError(err))
      .finally(() => setLoading(false));
  }, [resourceName]);

  return { allEnvDetails, isLoading };
};

What I am trying to achieve - Whenever the resourceName changes, it will need to refresh and make calls to all different environments (dev, stage and prod), and return the latest allEnvDetails back. It should avoid other re-renders

Ideally, there should only be 3 axios.get calls that correspond to the 3 environments. However, it's being called 9 times (3 times for each environment).

According to this SO question, I am using the setState within a promise resolve/reject block, so whenever state within the functional component (my hook) is updated it will trigger another, unwanted re-render.

Is there a better way to refactor/resolve this issue? I had been searching for a while, but I'm not sure what can be improved.

2 Answers

you can try to add condition if(isLoading) in begining of function:

`
React.useEffect(() => {
if(isLoading){
const configPromises = ['dev', 'stage', 'prod'].map((environment: 
Environment) =>
axios.get('someUrlGetEndpoint'));

Promise.all(configPromises)
  .then((resp: ResourceDetails[]) => setAllEnvDetails(resp))
  .catch((err: AxiosError) => setError(err))
  .finally(() => setLoading(false));
}
}, [resourceName]);

`

Try this code:

export const useConfigDetails = (resourceName: string) => {
  const [isLoading, setLoading] = React.useState<boolean>(true);
  const [error, setError] = React.useState<AxiosError>(undefined);
  const [allEnvDetails, setAllEnvDetails] = React.useState<ResourceDetails[]>();


  React.useEffect(() => {
      (async () => {
          try{
              setLoading(true)

              const dev = await axios.get('someUrlGetEndpoint');
              const stage = await axios.get('someUrlGetEndpoint');
              const prod = await axios.get('someUrlGetEndpoint');

              setAllEnvDetails([
                  dev,
                  stage,
                  prod
              ])

              setLoading(false)
          }catch(e) {
              setError(e);
              setLoading(false)
          }
      })()
  }, [resourceName]);

  return { allEnvDetails, isLoading };
};
Related