【问题标题】:using useEffect in combination with Axios fetching API data returns null - how to deal with this?使用 useEffect 结合 Axios 获取 API 数据返回 null - 如何处理?
【发布时间】:2019-07-18 23:53:02
【问题描述】:

在实际对象加载之前,使用 Axios 和 useEffect 获取数据会导致 null。

不幸的是,在实际对象不为空之前,我无法使用对象解构。

我不得不使用钩子来检查一个对象是否为空。

例如,我想创建多个函数并将我的代码拆分为单独的函数以提高可读性。

这是我的 HTTP 请求 Hook:

import { useState, useEffect } from 'react';

import axios from 'axios';

export const useHttp = (url, dependencies) => {
    const [isLoading, setIsLoading] = useState(false);
    const [fetchedData, setFetchedData] = useState(null);

    useEffect(() => {
        setIsLoading(true);

        axios
            .get(url)
            .then(response => {
                setIsLoading(false);
                setFetchedData(response.data);
            })
            .catch(error => {
                console.error('Oops!', error);
                setIsLoading(false);
            });
    }, dependencies);

    return [isLoading, fetchedData];
};

后面是我的页面组件:

import React from 'react';

import { PAGE_ABOUT, API_URL } from 'constants/import';

import Header from './sections/Header';
import Main from './sections/Main';
import Aside from './sections/Aside';

import { useHttp } from 'hooks/http';

const About = () => {
    const [isLoading, about] = useHttp(PAGE_ABOUT, []);

    if (!isLoading && about) {
        return (
            <section className="about">
                <div className="row">
                    <Header
                        featuredImage={API_URL + about.page_featured_image.path}
                        authorImage={API_URL + about.page_author_image.path}
                        authorImageMeta={about.page_author_image.meta.title}
                        title={about.about_title}
                        subtitle={about.about_subtitle}
                    />

                    <Main
                        title={about.page_title}
                        content={about.page_content}
                    />

                    <Aside
                        title={about.about_title}
                        content={about.about_content}
                    />
                </div>
            </section>
        );
    }
};

export default React.memo(About);

实际问题是我无法在对象实际返回之前嵌套函数。

有没有办法在不检查的情况下获取数据?或者更清洁的解决方案会有所帮助。

我想使用多个组件来拆分代码。

任何意见或建议将不胜感激。

【问题讨论】:

  • 我真的不确定代码的哪一部分给您带来了问题?你能再解释一下吗?
  • 我和 Zeljko 在同一条船上,我不确定实际的问题/问题是什么。
  • 我有这个“if (!isLoading && about) {”我无法在这个 IF 语句之外添加箭头函数,因为它首先返回 null 导致我的应用程序返回未定义。我想使用对象解构而不是使用 about.title,about.section 我只想使用“title”和“section”。而且我想将完整的代码拆分为多个内联组件,但我不能在 if 语句之外执行此操作。有没有办法只在没有检查的情况下加载数据?
  • 你能创建一个 stackblitz/codesandbox 演示来展示这个问题吗?

标签: reactjs


【解决方案1】:

您的要求对 UI 不利。您不想在获取数据时阻止 UI 呈现。所以通常的做法是显示一个加载微调器,或者(如果您指望请求很快)在它弹出之前什么都不渲染。

所以你会有类似的东西:

const About = () => {
    const [isLoading, about] = useHttp(PAGE_ABOUT, []);

    if (isLoading) return null; // or <Loading />

    return (
       <section className="about">
            <div className="row">
                <Header
                    featuredImage={API_URL + about.page_featured_image.path}
                    authorImage={API_URL + about.page_author_image.path}
                    authorImageMeta={about.page_author_image.meta.title}
                    title={about.about_title}
                    subtitle={about.about_subtitle}
                />

                <Main
                  title={about.page_title}
                  content={about.page_content}
                />

                <Aside
                  title={about.about_title}
                  content={about.about_content}
                />
            </div>
        </section>
    );
};

如果您的 api 没有对错误的保护并且您害怕成为 nullundefined 您可以使用 Error Boundary 组件包装组件并显示默认错误。但这取决于您是否在应用中使用这些功能。

错误边界是捕捉 JavaScript 错误的 React 组件 在其子组件树中的任何位置,记录这些错误并显示 一个后备 UI 而不是崩溃的组件树。错误 边界在渲染、生命周期方法和 它们下面的整棵树的构造函数。

【讨论】:

    【解决方案2】:

    如果您在我们的 About 组件中的条件为 false,则您不会返回任何内容,因此如果您可以添加 isLoading!about 则添加一个案例装载机可能:

    const About = () => {
        const [isLoading, about] = useHttp(PAGE_ABOUT, []);
    
        let conditionalComponent =  null;
    
        if (!isLoading && about) {
            conditionalComponent= (
                <section className="about">
                    <div className="row">
                        <Header
                            featuredImage={API_URL + about.page_featured_image.path}
                            authorImage={API_URL + about.page_author_image.path}
                            authorImageMeta={about.page_author_image.meta.title}
                            title={about.about_title}
                            subtitle={about.about_subtitle}
                        />
    
                        <Main
                            title={about.page_title}
                            content={about.page_content}
                        />
    
                        <Aside
                            title={about.about_title}
                            content={about.about_content}
                        />
                    </div>
                </section>
            );
        }
        return conditionalComponent
    
    };
    

    【讨论】:

      【解决方案3】:

      我知道你想摆脱条件if (!isLoading &amp;&amp; about) {

      您可以通过多种方式实现:

      1) 您可以自己创建Conditional 组件。这个组件应该接收children props 作为一个函数并且应该将其他的props 应用到这个函数:

      function Conditional({ children, ...other }) {
        if (Object.entries(other).some(([key, value]) => !value)) {
          return "empty"; // null or some `<Loading />` component
        }
        return children(other);
      }
      

      另外我做了一个小demohttps://codesandbox.io/s/upbeat-cori-ngenk

      它可以帮助您只为条件创建一个位置并在数十个组件中重用它:)

      2) 你可以使用一些构建在 React.lazy 和 React.Suspense 之上的库(但它不能在服务器端工作)。 例如https://dev.to/charlesstover/react-suspense-with-the-fetch-api-374j 和一个库:https://github.com/CharlesStover/fetch-suspense

      【讨论】:

        猜你喜欢
        • 2021-10-28
        • 1970-01-01
        • 2018-09-03
        • 1970-01-01
        • 2019-08-20
        • 2022-01-05
        • 2021-06-14
        • 1970-01-01
        • 2021-09-05
        相关资源
        最近更新 更多