skip to Main Content

When trying to asynchronously make two API calls, my useState() for the second API call is not updating on the page until the page re-renders. I am trying to check if the page is loading before rendering with isLoading.

I’ve tried a few different promise/asynchronous approaches – even creating my own unoptimized version of a promise with setTimeout()

isLoading

  const [isLoading, setIsLoading] = useState(false);

Api Calls

useEffect(() => {
    setIsLoading(true);
    (async () => {
      await axios.get(`http://localhost:4242/player/${id}`).then((res) => {
        const data = res.data;
        setPlayer({ ...data });
      });
      await axios
        .get(`http://localhost:4242/team/${player.team_ID}`)
        .then((res) => {
          const data = res.data;
          setTeam({ ...data });
          setIsLoading(false);
        });
    })();
  }, []);

Render

<>
  <Navbar/>
  {isLoading && <Spinner/>}
  {player && team && <Player/>}
  <LoginWidget/>
  <Footer/>
</>

Edit: I was able to fix this issue by using response data from the first API call instead of trying to access the useState() variable. I also switched this to a trycatch.

useEffect(() => {
    const fetchData = async () => {
      setIsLoading(true);
      try {
        const { data: playerRes } = await axios.get(
          `http://localhost:4242/player/${id}`
        );
        setPlayer({ ...playerRes });
        const { data: teamRes } = await axios.get(
          `http://localhost:4242/team/${playerRes.team_ID}`
        );
        setTeam({ ...teamRes });
      } catch (err) {
        console.log(err);
      }
      setIsLoading(false);
    };
    fetchData();
  }, []);

2

Answers


  1. Without a reproducible example, it is tricky to say why this is happening. Maybe it’s because isLoading is set to false only after the second API call completes, which might not sync well with the initial render (or re-renders) of the React component.

    Either way, as the two API calls don’t depend on each other, it would be better to use Promise.all to fire them off at the same time and then proceed only when both have completed.

    This is how I would do it:

    useEffect(() => {
      setIsLoading(true);
      Promise.all([
        axios.get(`http://localhost:4242/player/${id}`),
        axios.get(`http://localhost:4242/team/${player.team_ID}`)
      ])
      .then(([playerRes, teamRes]) => {
        setPlayer({ ...playerRes.data });
        setTeam({ ...teamRes.data });
        setIsLoading(false);
      })
      .catch(error => {
        // handle errors
      });
    }, []);
    
    Login or Signup to reply.
  2. Since you’re using await, you can also try the following:

    useEffect(() => {

    (async () => {
         setIsLoading(true);
    
         const resp1 = await axios.get(`http://localhost:4242/player/${id}`);
         const data1 = resp1.data;
    
         const resp2 = await axios.get(`http://localhost:4242/team/${player.team_ID}`);
         const data2 = resp2.data;
    
         if (data1 && data2) {
              setPlayer({ ...data1 });
              setTeam({ ...data2 });
              setIsLoading(false);
         }
    
    })();
    

    }, []);

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search