1

I am attempting to make many HTTP calls in a component using useEffect and useState to store the results of this http call. I thought I had a basic understanding of how useEffect works but I am confused as to why my code isn't working and therefore must be implementing it wrong.

Here is my useEffect hook code in my dashboard component:

useEffect(() => {       
    isMounted.current = true;

    const fetchData = async () => {
      if (survey === null) {
        const surveyResponse = await getSurveys('standard');
        setSurvey(surveyResponse.data);
      }

      if (teamInfo === null) {
        const teamInfoResponse = await getTeamInfo(user.teamId);
        setTeamInfo(teamInfoResponse.data);
      }

      if (week === null || year === null) {
        const weekAndYearResponse = await getWeekAndYear();
        setWeek(weekAndYearResponse.data.weekNumber);
        setYear(weekAndYearResponse.data.year);
      }

      if (week && year && !stats) {
        const statsResponse = await getTeamSurveyStats(week, year, user.teamId);
        setStats(statsResponse.data);
      }
    };

    fetchData();

    return () => {
      // executed when unmount
      isMounted.current = false;
    };
  }, [survey, teamInfo, week, year, stats, user.teamId]);

I thought that checking to see if the state value I was setting for the return of each call, I could chuck this http call inside a conditional and it would only make the http request on re-render if that variable was null, as I define this using useState(null).

When I check my network tab I see the following:

enter image description here

The first call only runs once which is correct - this is getSurveys the second call runs twice which is incorrect as only needs to run once - this is getTeamInfo the third call runs 5 times which is strange as only needs to run once - this is getWeekAndYear the fourth call runs only once which is correct.

Can anyone see why 2 of these calls are running several times? Thanks

James
  • 2,800
  • 7
  • 45
  • 81

3 Answers3

1

I will try to walk you through what happens on the first 2 calls so that you're able to extrapolate what happens for the rest.

When your Dashboard element loads for the first time, useEffect runs your code. Specifically, it runs this function fetchData.

fetchData sees that survey is null and runs this code:

const surveyResponse = await getSurveys('standard');
setSurvey(surveyResponse.data);

When that code calls setSurvey(surveyResponse.data); and updates your state variable, which I assume is survey, useEffect runs again. And it does so before finishing the original on-mount run. So now you have 2 fetchData calls in the pipe, one of which is partially finished. We will call them 1st and 2nd.

The 1st is now at this block and is likely awaiting the getTeamInfo call:

const teamInfoResponse = await getTeamInfo(user.teamId);
setTeamInfo(teamInfoResponse.data);

Meanwhile, the 2nd swoops in and goes straing into the same block of code (because survey is no longer null, but teamInfo still is). So now both 1st and 2nd are running const surveyResponse = await getSurveys('standard');

Hence the 2 calls for getTeamInfo.

Then this:

setTeamInfo(teamInfoResponse.data);

triggers another useEffect run, this time because teamInfo got updated. I think you get the picture...

codemonkey
  • 7,325
  • 5
  • 22
  • 36
  • Hi, Yes that makes complete sense. I have only been using React for 9 months after using Angular for 7+ years so hooks are relatively new to me. I thought once a rerender was called after setting the first state variable the original useEffect call was cancelled! – James Mar 02 '21 at 12:02
0

The useEffect hook is called each time when some of the values passed in the array are changed. It will be called the second time after the survey is changed, but timeInfo isn't loaded at that time, so the request will be duplicated, and this will happen for each other variables. Also, this is answered here https://stackoverflow.com/a/54120692/14349629.

0

useEffect is called each time any variable of the array(which is passed as a second parameter).

Try following code

useEffect(() => {       
    isMounted.current = true;

    const fetchData = async () => {
      if (survey === null) {
        const surveyResponse = await getSurveys('standard');
        setSurvey(surveyResponse.data);
        return;
      }

      if (teamInfo === null) {
        const teamInfoResponse = await getTeamInfo(user.teamId);
        setTeamInfo(teamInfoResponse.data);
        return;
      }

      let weekAndYearResponse = {};
      if (week === null || year === null) {
        weekAndYearResponse = await getWeekAndYear();
        setWeek(weekAndYearResponse.data.weekNumber);
        setYear(weekAndYearResponse.data.year);
      }

      if (weekAndYearResponse.data.weekNumber && weekAndYearResponse.year && !stats) {
        const statsResponse = await getTeamSurveyStats(weekAndYearResponse.data.weekNumber, weekAndYearResponse.year, user.teamId);
        setStats(statsResponse.data);
      }
    };

    fetchData();

    return () => {
      // executed when unmount
      isMounted.current = false;
    };
  }, [user.teamId]);
First Arachne
  • 786
  • 1
  • 8
  • 18