如何在 reactJS 中呈现我的页面之前正确进行两个 API 调用?

How do I correctly make two API calls prior to rendering my page in reactJS?

提问人:ConnorWitmer 提问时间:10/29/2023 最后编辑:ConnorWitmer 更新时间:10/29/2023 访问量:39

问:

尝试异步进行两个 API 调用时,在页面重新呈现之前,我对于第二个 API 调用不会在页面上更新。我正在尝试在使用 .useState()isLoading

我尝试了几种不同的 promise/异步方法——甚至使用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/>
</>

编辑:我能够通过使用第一个 API 调用的响应数据而不是尝试访问变量来解决此问题。我还将其切换到 trycatch。useState()

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();
  }, []);
JavaScript ReactJS 异步

评论


答:

1赞 James Hibbard 10/29/2023 #1

如果没有可重现的例子,很难说出为什么会发生这种情况。也许是因为设置为仅在第二个 API 调用完成后,这可能与 React 组件的初始渲染(或重新渲染)不能很好地同步。isLoadingfalse

无论哪种方式,由于这两个 API 调用不相互依赖,因此最好同时使用它们来触发它们,然后仅在两个调用都完成后才继续。Promise.all

我是这样做的:

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
  });
}, []);

评论

0赞 ConnorWitmer 10/29/2023
谢谢你,我将来将用于我不需要相互协作的 API 调用。不过,为此,我需要从我的播放器 API 调用中访问该值。我能够通过使用第二次调用的响应数据来修复它,我假设我的问题是由于useEffect,我在更新数据之前尝试访问数据。但是,当使用响应中的数据呈现所有内容时,它会按预期工作。谢谢!Promise.all()team_ID
0赞 James Hibbard 10/29/2023
是的,最初的问题似乎源于在更新之前尝试使用。通过直接使用从第一个 API 调用返回的值,这可确保将正确的 ID 用于第二个 API 调用。很高兴你让它工作:)player.team_ID
0赞 Claudio Puggioni 10/29/2023 #2

由于您使用的是 await,因此还可以尝试以下操作:

使用效果(() => {

(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);
     }

})();

}, []);