如何检查 useEffect 中的数据加载

Posted

技术标签:

【中文标题】如何检查 useEffect 中的数据加载【英文标题】:How to check data loading in useEffect 【发布时间】:2021-10-12 00:26:37 【问题描述】:

我在我的 React 组件中的 useEffect() 中遇到了一个奇怪的问题。当页面加载时,我必须发出 2 个单独的 axios 请求来获取数据。我正在尝试使用挂钩变量来查看在将数据对象传递给 JSX 之前是否已填充它们。这是我当前的配置:

import React,  useState, useEffect  from 'react';
import Navbar from '../components/layout/Navbar';
import ContactsCard from '../components/layout/ContactsCard';
import EmailCard from '../components/layout/EmailCard';
import MeetingsCard from '../components/layout/MeetingsCard';
import  useParams  from "react-router-dom";
import config from './../config/config';
import axios from "axios";

function SummaryPageNew() 
    let  selectName  = useParams();
    const [contactData, setContactData] = useState();
    const [meetingData, setMeetingData] = useState();
    const [loadingData, setLoadingData] = useState(true);

    //API calls
    async function getContactData() 
        axios
        .get(config.apiURL + `/affiliations/name/$selectName`)
        .then((response) => 
            return setContactData(response.data[0]);

        );
    

    async function getMeetingData() 
        axios
        .get(config.apiURL + `/meetings_attendees/name/$selectName`)
        .then((response) => 
            return setMeetingData(response.data);
        );
    

    useEffect((loadingData) => 
        getContactData();
        getMeetingData();
        setLoadingData(false);

        if (loadingData) 
            //if the result is not ready so you make the axios call
            getContactData();
            getMeetingData();
            setLoadingData(false);
        
    , []); // eslint-disable-line react-hooks/exhaustive-deps

    return (
        <div>
            <Navbar />
            <div>
                <div style= textAlign: "center" >
                    <h3>Contact Information</h3>
                    <h5>Profile: selectName</h5>
                </div>
                loadingData ? (
                    <p>Loading Please wait...</p>
                ) : (
                    <div className="row">
                        <ContactsCard contactData=contactData />
                        <EmailCard emailData=meetingData />
                        <MeetingsCard meetingData=meetingData />
                    </div>
                )
            </div>
        </div>
    )


export default SummaryPageNew

我尝试在 axios 调用中移动 setLoadingData(false) 方法。如果我将它移到getMeetingData() 调用中。这工作......有时。显然,在某些情况下,它会先加载,然后 contactData 不会返回。在当前配置中,DOM 以“Loading Please wait...”呈现。我在这里做错了什么?我该如何解决这个问题?

【问题讨论】:

您不需要专门将loadingData 传递给useEffect((loadingData) =&gt; ,它已经在您的组件顶部,您可以使用它。 【参考方案1】:

你的代码有很多问题。

    useEffect 函数不带任何参数。您将 loadingData 声明为参数覆盖了组件中的实际 loadingData 变量,React 不会为此传递值。

    您在调用 useEffect 时缺少对 loadingData 的依赖。照原样,只要组件保持挂载,该函数将只执行一次,然后再也不会执行。因此,loadingData 永远不会设置为 false。通常,除非您有充分的理由,否则避免警告有关 useEffect 依赖项是一个坏主意。

我推荐的解决方案是避免为“加载”状态存储额外的状态。相反,我只检查两个状态值是否已被填充,如果没有,则显示“正在加载...”文本。

这给你留下了:

function SummaryPageNew() 
    let  selectName  = useParams();
    const [contactData, setContactData] = useState();
    const [meetingData, setMeetingData] = useState();
    const isReady = contactData !== undefined && meetingData !== undefined;

    //API calls
    async function getContactData()  ... 

    async function getMeetingData()  ... 

    useEffect((loadingData) => 
        getContactData();
        getMeetingData();
    , []);

    return (
        <div>
            <Navbar />
            <div>
                <div style= textAlign: "center" >
                    <h3>Contact Information</h3>
                    <h5>Profile: selectName</h5>
                </div>
                isReady ? (
                    <div className="row">
                        <ContactsCard contactData=contactData />
                        <EmailCard emailData=meetingData />
                        <MeetingsCard meetingData=meetingData />
                    </div>
                ) : (
                    <p>Loading Please wait...</p>
                )
            </div>
        </div>
    )

react-query 是一个非常强大的库,用于使用钩子异步获取数据。这避免了必须管理很容易不同步的复杂状态。但是,我会先学习 react hooks 的基础知识!

【讨论】:

这些都是很好的答案,但我选择了这个解决方案,因为它是最简单的。谢谢。【参考方案2】:

您正在处理异步函数调用。 javascript 在继续执行您的程序之前不会等待您的异步函数完成。这意味着您的调用可能仍在获取,而您已经将 loadingData 设置为 false。您可以通过使用 Promise.all 在异步函数解析时获取回调来解决此问题:

//API calls
async function getContactData() 
  return axios
    .get(config.apiURL + `/affiliations/name/$selectName`)
    .then((response) => 
      return setContactData(response.data[0]);
    );


async function getMeetingData() 
  return axios
    .get(config.apiURL + `/meetings_attendees/name/$selectName`)
    .then((response) => 
      return setMeetingData(response.data);
    );


useEffect(() => 
  let mounted = true

  return () =>  mounted = false 
  
  Promise.all([getContactData(), getMeetingData()]).then(() =>  
    if (mounted) setLoadingData(false) 
  ) 
, []); // eslint-disable-line react-hooks/exhaustive-deps

还要注意我添加的let mounted = true:您要确保在异步调用完成时该组件仍然存在。例如,如果通话需要一段时间,那么您可能会离开并不是不可想象的。

最后,禁用react-hooks/exhaustive-deps 并非明智之举。通过一些更改,您可以将代码设置为不再需要忽略。

React 希望你在依赖数组中提供getContactDatagetMeetingData。您可以通过将数据获取功能移到组件之外来解决此问题。这意味着他们不再有权访问 selectName 变量,但您可以将该变量作为参数提供:

function SummaryPageNew() 
  let  selectName  = useParams();
  const [contactData, setContactData] = useState();
  const [meetingData, setMeetingData] = useState();
  const [loadingData, setLoadingData] = useState(true);

  //API calls

  useEffect(() => 
    let mounted = true

    Promise.all([
      getContactData( selectName ), 
      getMeetingData( selectName )
    ]).then(([contactData, meetingData]) => 
      if (!mounted) return
      setContactData(contactData)
      setMeetingData(meetingData)
      setLoadingData(false)
    )

    return () =>  mounted = false 
  , [selectName]);

  return () // Render your component


async function getContactData( selectName ) 
  return axios
    .get(config.apiURL + `/affiliations/name/$selectName`)
    .then((response) => 
      return setContactData(response.data[0]);
    );


async function getMeetingData( selectName ) 
  return axios
    .get(config.apiURL + `/meetings_attendees/name/$selectName`)
    .then((response) => 
      return setMeetingData(response.data);
    );

【讨论】:

你可能想看看 react-query,它解决了异步函数获取数据时出现的很多问题。

以上是关于如何检查 useEffect 中的数据加载的主要内容,如果未能解决你的问题,请参考以下文章

手动刷新页面时如何强制app.js中的useEffect在其他组件的useEffect之前加载

在 React 中调用 useEffect 时重新加载照片

使用 useEffect 在页面加载时从上下文中获取数据

如何在useEffect钩子反应中停止内存泄漏

如何在 react.js 中的页面加载时显示引导模式?

当我想检查窗口对象的属性是不是在这里时,useEffect 中的无限循环