【问题标题】:How to check data loading in useEffect如何检查 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; {,它已经在您的组件顶部,您可以使用它。

标签: reactjs axios use-effect


【解决方案1】:

你的代码有很多问题。

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

  2. 您在调用 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,它解决了异步函数获取数据时出现的很多问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-02
  • 1970-01-01
  • 2020-10-09
  • 1970-01-01
相关资源
最近更新 更多