在React中获取数据

2020年12月30日11:19:17 发表评论 33 次浏览

本文概述

React擅长在分层组件视图中显示数据。但是你的组件如何获取数据?有很多解决方法, 每种方法各有优缺点。

在本教程中, 我们将重点介绍React中的数据获取。我们将通过代码示例演示如何在React中获取数据, 并介绍一些方法来帮助你确定适用于React项目的最佳数据获取方法。

我们将详细介绍以下内容:

  • 如何在React中获取数据
  • 提取服务器提供的数据
  • React组件如何获取数据
  • 使用提取API
  • 如何使用Axios提取数据
  • Fetch是否比Axios更好?
  • 如何在React中使用异步/等待获取数据
  • 如何在React中从GraphQL API获取数据
  • 如何获取高阶组件中的数据
  • 如何在渲染道具中获取数据
  • 使用React Hooks获取数据
  • 并发模式和反应中止

为了展示如何在React中获取数据, 我们将构建一个简单的React应用, 其中包含可以从中获取用户的组件JSONPlaceholder。所有组件均呈现相同的数据, 并且外观相同。我用了React Bootstrap表所有组件的实际渲染, 但是组件在获取数据的方式上有很大的不同。

这是我们的示例应用程序的外观:

React数据获取示例应用程序

主要的应用程序组件只是一个功能组件。它呈现了各种数据模式组件, 这些组件说明了每种数据获取方法:

import React from ‘react’;
import ‘./App.css’;
import UserTableAutonomous from "./components/UserTableAutonomous";
import UserTableHOC from "./components/UserTableHOC";
import UserTableReactHooks from "./components/UserTableReactHooks";
import UserTableRenderProps from "./components/UserTableRenderProps";
import SimpleUserTable from "./components/SimpleUserTable";
function App() {
 return (
   <div className=’App’>
     <h2> User Table — Autonomous</h2>
     <UserTableAutonomous/>
     <h2> User Table — High Order Component</h2>
     <UserTableHOC/>
     <h2> User Table — Render Props</h2>
     <UserTableRenderProps children={SimpleUserTable}/>
     <h2> User Table — React Hooks</h2>
     <UserTableReactHooks/>
   </div>
 );
}
export default App

你可以在以下位置访问完整的源代码亚搏体育app.

事不宜迟, 让我们开始吧。

如何在React中获取数据

如果你刚开始使用React, 则可能只需要从事简单的, 不需要访问数据的初学者级项目。在你进行React旅程并学习如何构建更复杂的项目时, 你的应用程序几乎肯定会需要此功能。实际上, 数据获取是几乎每个React应用程序的核心要求。

我们为制作了一个自定义演示.
不完全是。点击这里查看.

在React中获取数据1

有多种方法可以在React中获取数据, 包括使用内置的Fetch API, Axios, async / await等。我们将详细介绍所有这些方法。你还可以从GraphQL后端等中获取更高阶组件中的数据并渲染道具。继续阅读以学习方法。

有关帮助你学习React中数据获取基础知识的直观指南, 请查看此全面, 直接的视频教程:

提取服务器提供的数据

这是获取应用程序数据的传统方法。数据嵌入在从服务器发送的HTML中。如果你想获取新数据, 则需要手动刷新页面或定期刷新页面。记住这一点?

<meta http-equiv="refresh" content="30">

对于React应用程序而言, 它并不特别相关, 它具有更多动态, 更细粒度的自身更新方式, 但它仍然是从服务器向浏览器获取数据的合法方式。许多旧版Web应用程序仍在使用它, 并且如果禁用了JavaScript或必须使用旧的浏览器, 由于它非常简单明了, 它甚至可能是最好的方法。

React组件如何获取数据

React组件只能获取自己的数据。最大的问题是何时获取数据。有几种选择:

  • 从无数据开始, 然后根据用户操作(例如单击按钮)获取数据
  • 一次加载数据
  • 定期加载数据

由于该组件是完全自主的, 因此没有其他组件可以告诉它该加载数据了。在这种情况下, 我选择了第一次将数据加载到componentDidMount()并且还设置了一个计时器, 该计时器每五秒钟再次获取一次数据。

让我们看一下UserTableAutonmous组件, 并对其进行逐一分解。这是一个基于类的标准React组件。其状态包括两个字段:布尔值isFetching由于尚未获取, 因此已初始化为false, 并初始化为空用户列表, 这是它要获取的数据。

class UserTableAutonomous extends Component {
    constructor(props) {
        super(props);
        this.state = {
            isFetching: false, users: []
        };
    }

的render()方法呈现引导表组件, 从状态传递当前用户。如果正在抓取中, 还会显示"正在抓取用户…"消息。这是超基本的进度报告。引导表将仅显示每个用户的ID, 名称和用户名字段, 尽管还有其他几个字段。

render() {
        return (
            <div>
                <BootstrapTable data={this.state.users} 
                                trClassName={rowClassNameFormat}>
                    <TableHeaderColumn isKey dataField='id' />
                    <TableHeaderColumn dataField='name' />
                    <TableHeaderColumn dataField='username' />
                </BootstrapTable>
                <p>{this.state.isFetching ? 'Fetching users...' : ''}</p>
            </div>
        )
    }

正如我之前所讨论的, 实际的数据获取发生在componentDidMount(), 这是在安装组件并准备就绪时调用的React生命周期方法。有人可能会说最好使用componentWillMount(), 即将安装组件时调用该方法, 并提早开始提取数据以节省时间。但是, 有两个重要的理由反对它。

第一, 不推荐使用从React 17开始。其次, 当你在以下位置使用Fetch API或Axios时componentWillMount(), React会在不等待渲染完成的情况下进行渲染, 这将导致第一次渲染为空白-因此你实际上并没有节省任何时间。

注意componentDidMount()在第一个渲染之后被调用, 因此你仍然需要处理第一个空渲染。在我们的演示中, 我使用"正在获取用户…"消息。另一种选择是在构造函数中获取初始数据, 但这会延迟组件的首次渲染。

好了, 它已经解决—我们将在componentDidMount()。该代码只是调用fetchUsers()方法并启动一个计时器, 该计时器将调用fetchUsers()每五秒钟。

componentDidMount() {
        this.fetchUsers();
        this.timer = setInterval(() => this.fetchUsers(), 5000);
    }

的componentWillUnmount()当组件消失时会调用方法, 现在正是通过调用来停止计时器的好时机clearInterval()并将其设置为null。

componentWillUnmount() {
        clearInterval(this.timer);
        this.timer = null;
    }

的fetchUsers()设置isFetching状态变量true, 因此在获取新数据时, 该组件将呈现"正在获取用户…"消息。然后通过一些"魔术"吸引用户isFetching回到false.

fetchUsers() {
     this.setState({...this.state, isFetching: true});
        users = <REDACTED>
     this.setState({...this.state, isFetching: false});
        // error handling
        <REDACTED>
    }

我不是自动组件的忠实拥护者;他们太黑了。它们混合了两个截然不同的数据获取和数据显示问题, 并且更难测试。

有多种方法可以实现fetchUsers()功能。我在不同的组件中使用了三种不同的实现。这三个实现都完成相同的任务:

  • 内置的Fetch API
  • Axios
  • 异步/等待+ Axios

我本来可以在React Fetch API中使用async / await。我在不同的组件中随意使用了不同的实现;它们都是可以互换的。利弊比功能更符合人体工程学。

让我们仔细看看这些替代实现。

使用提取API

我已经在UserTableHOC零件。我实际上叫做函数fetchUsersWithFetchAPI(), 但将其分配给名为fetchUsers, 因此组件只调用fetchUsers().

该功能通过设置isFetching变量为true, 然后调用提取。提取将返回一个承诺, 该承诺将解决为响应。回应的json()方法返回一个JavaScript对象。然后将用户设置为状态并重置isFetching虚假。如果出了什么问题, 则catch处理程序会将错误记录到控制台, 并在提取完成后重置isFetching变量。

fetchUsersWithFetchAPI = () => {
        this.setState({...this.state, isFetching: true});
        fetch(USER_SERVICE_URL)
            .then(response => response.json())
            .then(result => {
                this.setState({users: result, isFetching: false})
            })
            .catch(e => {
                console.log(e);
                this.setState({...this.state, isFetching: false});
            });
    };
fetchUsers = this.fetchUsersWithFetchAPI

它非常冗长和繁琐, 但是它是标准的, 并且没有外部依赖项-这就是Fetch API的卖点。再说一遍, 这是JavaScript;许多依赖性是土地法则。输入Axios。

如何使用Axios提取数据

我已经将Axios用于UserTableRenderProps零件。 Axios还具有类似于Fetch的基于promise的API, 但是Axios保存JSON解析阶段并处理所有错误。例如, Fetch API返回404作为正常响应, 因此你需要检查代码中的响应, 并在需要时自行引发错误。

fetchUsersWithAxios = () => {
        this.setState({...this.state, isFetching: true});
        axios.get(USER_SERVICE_URL)
            .then(response => {
                this.setState({data: response.data, isFetching: false})
            })
            .catch(e => {
                console.log(e);
                this.setState({...this.state, isFetching: false});
            });
    };
fetchUsers = this.fetchUsersWithAxios

该代码几乎与Fetch API版本完全相同, 只差了一步, 并提供了更强大的错误处理功能。

Fetch是否比Axios更好?

你可以通过HTTP协议与服务器通信使用Fetch API或Axios。有什么不同?

Fetch API提供了一个取()在window对象上定义的方法, 以及用于访问和处理HTTP请求和响应的JavaScript接口。取()仅具有一个强制性参数:要获取的资源的URL。它返回一个可用于检索请求响应的promise。

另一方面, Axios是一个JavaScript库, 使你可以从Node.js或XML发出HTTP请求。它支持JavaScript ES6中的Promise API。 Axios使你能够拦截HTTP请求和响应, 保护客户端免受跨站点请求伪造(XSRF)的影响, 并能够取消请求。

So哪种React数据获取方法更好:取()还是Axios?在使用内置API时, 这取决于项目的特定要求和你的舒适度。

Axios在紧凑的程序包中提供了易于使用的API, 可满足你大多数的HTTP通信需求。但是, 如果你喜欢使用本机API, 则不会阻止你实施Axios功能。当然, 可以通过使用Axios库来重现Axios库的关键功能。取()Web浏览器提供的方法。最终, 是否值得加载客户端HTTP API取决于你是否愿意使用内置API。

如何在React中使用异步/等待获取数据

我在其中使用了async / await语法UserTable自治零件。这些promise链相对于旧的回调地狱是一个巨大的改进, 但是它可以变得更好。看看相同的代码在async / await中看起来多么漂亮自然:

async fetchUsersAsync() {
        try {
            this.setState({...this.state, isFetching: true});
            const response = await axios.get(USER_SERVICE_URL);
            this.setState({users: response.data, isFetching: false});
        } catch (e) {
            console.log(e);
            this.setState({...this.state, isFetching: false});
        }
    };
fetchUsers = this.fetchUsersAsync;

毫无疑问, 这是我最喜欢的变体。

如何在React中从GraphQL API获取数据

用户API是REST API。 GraphQL后端怎么样? GraphQL服务器通常也会通过HTTP返回JSON。

主要区别在于, 有一个查询端点可以获取数据(忽略突变和预订), 并且请求和返回的实际数据遵循GraphQL模式。数据获取策略和策略无法区分REST和GraphQL, 它们在两者上均能很好地工作。

现在, WebSockets与gRPC的故事有所不同-我们再待一天。

如何获取高阶组件中的数据

高阶组件是复合组件, 其中顶级组件负责获取数据并将其传播到子组件。高阶组件可以任意嵌套。

多个子代组件可能会接收所获取数据的不同部分, 而层次结构中的其他组件可能根本不会使用该数据。这是一个小图来说明这一点:

在高阶组件中反应数据获取

基本思想是将获取和分发数据的关注与实际对数据进行处理的关注区分开来。在多个组件需要数据的不同方面的情况下, 由于只提取一次数据, 因此效率也更高。让我们看看效果如何。

的SimpleUserTable组件对服务器, 生命周期方法, 数据获取或错误处理一无所知;它所做的只是在道具中接收用户列表, 并使用引导表零件。它确实了解用户对象的属性, 并且需要一个ID, 名称和用户名。

import React from 'react'
import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table'
import '../css/Table.css'
import '../../node_modules/react-bootstrap-table/dist/react-bootstrap-table-all.min.css'
function rowClassNameFormat(row, rowIdx) {
    return rowIdx % 2 === 0 ? 'Gold-Row' : 'Silver-Row';
}
const SimpleUserTable = (props) => {
    return (
        <div>
            <BootstrapTable data={props.data} 
                            trClassName={rowClassNameFormat}>
                <TableHeaderColumn isKey dataField='id' />               
                <TableHeaderColumn dataField='name' />
                <TableHeaderColumn dataField='username' />
            </BootstrapTable>
            <p>{props.isFetching ? 'Fetching users...' : ''}</p>
        </div>
    )
};
export default SimpleUserTable

有趣的是, 对用户对象的了解只是部分视图。从JSONPlaceholder返回的实际用户对象具有更多信息:

{
    "id": 1, "name": "Leanne Graham", "username": "Bret", "email": "Sincere@april.biz", "address": {
      "street": "Kulas Light", "suite": "Apt. 556", "city": "Gwenborough", "zipcode": "92998-3874", "geo": {
        "lat": "-37.3159", "lng": "81.1496"
      }
    }, "phone": "1-770-736-8031 x56442", "website": "hildegard.org", "company": {
      "name": "Romaguera-Crona", "catchPhrase": "Multi-layered client-server neural-net", "bs": "harness real-time e-markets"
    }
  }

的SimpleUserTable只关心ID, 名称和用户名。如果后端服务器添加更多信息或删除/重命名一些未使用的字段, 则完全可以。

那么, 什么获取实际数据呢?那将是UserTableHOC。它在其获取用户componentDidMount通过调用fetchUsers()更新用户的方法, 以及isFetching是状态。的render()方法只是将状态传递给孩子SimpleUserTable.

import React, {
  Component
} from 'react'
import SimpleUserTable from "./SimpleUserTable";
const USER_SERVICE_URL = 'https://jsonplaceholder.typicode.com/users';
class UserTableHOC extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isFetching: false, users: []
    };
  }
  render = () => < SimpleUserTable data = {
    this.state.users
  }
  isFetching = {
    this.state.isFetching
  }
  />;
  componentDidMount() {
    this.fetchUsers();
  }
  fetchUsers = < REDACTED >
}
export default UserTableHOC

实际上, 我们将UserTable自治分为两个嵌套的组件;代码几乎相同, 但是更加简洁。更重要的是, 如果我们希望拥有多个以不同方式显示用户数据的组件, 那么我们已经准备就绪。

例如, 如果我们要启用用户选择, 然后在另一个组件中显示所选用户的完整信息(例如, FullUserInfo), UserTableHOC只需将相关的用户信息传递给FullUserInfo零件。

听起来不错, 但是在这些情况下, 还有很多工作要做, 例如向HOC告知子组件中的选择, 以及通过深度嵌套的组件层次结构的属性传递获取的数据。

因此, HOC不仅负责获取数据, 还负责在层次结构中直接在其下方呈现组件, 并可能响应源自这些子项的事件。

我们的下一个数据模式解决了这些问题, 但它有其自身的取舍。

如何在渲染道具中获取数据

如果我们可以实现一个不了解应该对数据执行什么操作的通用数据提取程序, 该怎么办?事实证明这是一种常见的做法。诀窍是使用间接层。

俗话说:"你可以用计算机来解决计算机科学中的任何问题间接附加层……除了间接层过多的问题。"

React模式通常称为渲染道具。这个想法是将一个prop传递给一个组件, 该组件是一个函数, 而不是静态值或对象。接收对象将执行此道具, 通常在render()方法因此命名渲染道具。

你可以通过使用功能替换部分逻辑来深度定制目标组件工作方式的能力。如果你熟悉面向对象的设计模式, 则它类似于策略模式或模板方法模式。

的代码UserTableRenderProps非常类似于UserTableHOC。最大的不同是render()方法, 它调用props.children()功能。这提高了抽象级别, 因为组件不需要了解有关其子组件的任何信息。

import {
  Component
} from 'react'
import axios from 'axios'
const USER_SERVICE_URL = 'https://jsonplaceholder.typicode.com/users';
class UserTableRenderProps extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isFetching: false, data: []
    };
  }
  render = () => this.props.children(this.state);
  componentDidMount() {
    this.fetchUsers();
  }
  fetchUsers = < REDACTED >
}
export default UserTableRenderProps

太酷了, 但这意味着无论通过哪种渲染道具, 都需要了解内部结构。

什么时候使用渲染道具有意义?一个很好的例子是在一个深层次结构中, 数据获取组件可以共享一个缓存。在这种情况下, 有多个数据读取器具有不同的子代是有意义的, 而HOC则固定了子代(在render()HOC组件的方法)。

让我们再来看看App()App.js的功能组件, 它通过孩子们将道具提供给UserTableRenderProps。如你所见, 它需要了解SimpleUserTable并传递下去。

function App() {
    return (
        <div className='App'>
            <h2> User Table - Autonomous</h2>
            <UserTableAutonomous/>
            <h2> User Table - High Order Component</h2>
            <UserTableHOC/>
            <h2> User Table - Render Props</h2>
            <UserTableRenderProps children={SimpleUserTable}/>
            <h2> User Table - React Hooks</h2>
            <UserTableReactHooks/>
        </div>
    );
}

确保组件在生产中呈现

调试React应用程序可能很困难, 尤其是在状态复杂时。如果你有兴趣监视和跟踪生产中所有用户的Redux状态,

尝试notlogy

.

在React中获取数据2
LogRocket仪表板免费试用横幅

日志火箭就像Web应用程序的DVR一样, 实际上记录了你网站上发生的一切。你可以汇总并报告问题发生时应用程序所处的状态, 而不用猜测为什么会发生问题。

notlogy Redux中间件软件包在你的用户会话中增加了一层可见性。 notlogy记录Redux存储中的所有操作和状态。

现代化如何调试React应用程序-免费开始监控.

使用React Hooks获取数据

React中的数据获取曾经需要带有状态和生命周期方法的基于类的组件。但React 16.8带给我们了挂钩.

诸如高阶组件和渲染道具之类的模式要求你重组组件层次结构和/或在整个层次结构中传播很多状态(直接与props或与各种包装器, 提供程序和使用者一起使用)。此外, 人们在与阶级及其实现方式上挣扎。

React Hooks的想法是将状态管理分为独立的功能, 这些功能不需要将状态的圆形固定在类生命周期方法的方孔中。

React的所有功能都可以在功能组件中使用, 不需要类。特别是, 我们可以使用React Hooks进行数据获取。

让我们检查一下UserTableReactHooks功能组件。

首先, useState()状态Hook以初始状态被调用。这类似于构造函数。挂钩返回两个值:当前状态和更新状态的函数。请注意, 你可以有多个状态挂钩, 如果你需要独立更新状态的不同部分, 这可能会很有用。

import React, {useEffect, useState} from 'react';
import axios from "axios";
import SimpleUserTable from "./SimpleUserTable";
const USER_SERVICE_URL = 'https://jsonplaceholder.typicode.com/users';
function UserTableReactHooks() {
    const [data, setData] = useState({users: [], isFetching: false});

到现在为止还挺好。为了执行数据获取等副作用, 我们将使用效果挂钩。默认情况下, 效果挂钩接受一个函数并在每个渲染之后运行它。

在这种情况下, 我希望它只运行一次, 因此我同时传递了一个函数和一个空数组。数组参数告诉Hook仅在数组中列出的状态变量发生更改时才应用效果(即运行函数)。因为我传递了一个空数组, 所以没有要监视的状态变量, 并且效果将只运行一次。

useEffect(() => {
        const fetchUsers = async () => {
            try {
                setData({users: data.users, isFetching: true});
                const response = await axios.get(USER_SERVICE_URL);
                setData({users: response.data, isFetching: false});
            } catch (e) {
                console.log(e);
                setData({users: data.users, isFetching: false});
            }
        };
        fetchUsers();
    }, []);

你可以将效果视为componentDidMount()和componentDidUpdate()基于类的组件。

最后, 它只返回SimpleUserTable与本地状态一起渲染。

return <SimpleUserTable data={data.users}
                            isFetching={data.isFetching}
    />
}
export default UserTableReactHooks

钩子是React的一个很酷的, 符合人体工程学的功能。我强烈建议你熟悉他们.

并发模式和反应中止

如果你是React开发人员, 那么到目前为止, 你最有可能听说过并发模式。挂起是并发模式中的一种机制, 它使你的组件可以在等待某些长时间运行的操作完成时将其显示为备用。显然, 数据获取是一项长期运行的操作, 你可能希望在获取数据时显示诸如消息, 进度条或微调框之类的内容。

换一种说法, 反应悬念是一组功能, 可帮助React应用保持响应状态, 而无论用户的设备功能或网络速度如何。它包装了你的自定义组件, 并使它们能够与React进行通信, 以使其在渲染组件之前正在等待一些数据加载。

暂停既不是像这样的数据获取库反应异步也没有状态管理工具a-laRedux。它只是防止你的组件呈现到DOM, 直到完成一些异步操作(即网络请求)为止。

请看以下示例:

<Suspense fallback={<p>loading...</p>}>
  <Todos />
</Suspense>

的托多斯组件包裹着悬念具有一个倒退支柱。如果托多斯正在等待异步操作, 例如从API检索待办事项列表, React渲染<p>载入中...</ p>改为DOM。的托多斯操作结束时呈现组件。

你可能会想使用以下代码尝试相同的操作:

...
if (loading) {
  return <p>loading...</p>
}

return <Todos />
...

这不是很有效, 因为它假设异步操作是由父组件触发的, 并且<托多斯 />操作完成后由此父组件呈现。但是如果托多斯触发了操作?

在这种情况下, 你需要将加载检查从父组件移到托多斯零件。假设有更多组件, 每个组件都会触发自己的异步请求。每个子组件都必须独立管理自己的加载状态, 这将使得在干净的工作流程中编排数据加载操作变得困难, 而这又不会导致用户体验过硬。

<Suspense fallback={<p>loading...</p>}>
  <Todos />
  <Tasks />
</Suspense>

在上面的示例中, 我们添加了另一个任务零件。假设此组件还触发了自己的异步操作。通过包装两个组件悬念, 实际上是在告诉React在解决这两个操作之前不要渲染任何一个组件。要在没有Suspense的情况下执行相同的操作, 你需要将异步调用移至父组件并添加一个if检查装货在呈现组件之前标记。

当然, 你可以混合使用各种方法-包括渲染时获取, 先渲染后获取和按需渲染。你可以通过阅读我们的综合文章来了解有关这些方法的更多信息React Suspense教程.

如果你已经具有某些以某种方式获取数据的组件, 以及使用其他方法的其他组件, 则它们都可以快乐地存在于同一应用程序中。但是, 如果你是从头开始, 那么使用React Hooks和Suspensesp可能是最好的选择。

请务必注意, React Suspense是一项实验性功能, 尚未在稳定版本中提供。

总结

本文涵盖了很多基础。我们探索了所有常见的React数据获取策略和策略。我们权衡了每种方法的利弊, 并在代码中演示了每种方法。

此时, 我将使用async / await语法使用React Hooks和Axios。在不久的将来, 签出Suspense进行数据提取将很有趣。

一盏木

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: