使用React-Async库进行声明式数据获取
介绍
在 JavaScript 应用程序中获取数据时,async-await
允许我们使用 命令式同步 编程来获取数据。 这意味着我们的代码将强制描述程序将如何运行以及将如何沿着单个操作线程运行。 另一方面,React 是一个以声明方式 构建 UI 的库。 这通常被认为是与命令式编程相反的方法,在命令式编程中,开发人员描述他们希望程序做什么,而不是他们希望它如何表现。 因此,React 和 async-await
的组合导致了一种混合模式,其中我们既有命令式代码(用于数据获取),也有声明性代码(用于 UI 组合)。
React-async 提供了一个声明式 API 来使用单个 React 组件执行任何 REST API 调用,从而允许在整个应用程序中使用声明式编程。 它负责处理错误、承诺解决和重试承诺,并处理本地异步状态。
在本文中,我们将解释 React-Async 库如何帮助我们获取数据并运行一些 Helper 组件和函数的演示。
使用 React-Async 以声明方式获取数据
让我们将我们的主题分为两部分:React 中的 声明式数据获取 和 异步 API 调用 。
声明式数据获取是一种用于调用 API 的方法,您可以在其中声明您希望它为您做什么,而无需担心与调用相关的所有事情。 它与命令式方法相反,您还需要详细说明您希望程序采取的步骤。
由于 JavaScript 默认是 同步的 并且是单线程的,所以当我们想要渲染一个组件来显示来自 React 之前版本的异步调用的一些数据时,需要使用类。 我们必须使用组件生命周期方法来确保调用在组件挂载时发生,然后我们使用本地状态来管理加载状态。
异步请求将等待请求响应,而其余代码继续执行。 然后,当时机成熟时,回调会将这些异步请求付诸行动。
让我们通过调用端点来获取货币价格列表来演示这一点:
import React, { Component } from 'react'; import axios from 'axios'; class App extends Component { state = { data: [], error: '', }; componentDidMount() { axios .get('https://api.coinmarketcap.com/v1/ticker/?limit=1') .then(res => this.setState({ data: res.data })) .catch(error => this.setState({ error })); } render () { return ( <div className="App"> <ul> {this.state.data.map(el => ( <li> {el.name}: {el.price_usd} </li> ))} </ul> </div> ); } } export default App;
注意: 通过在终端中输入“npm install axios”来安装 axios。
您可以使用 此 CodeSandbox 页面 实时查看此代码。
在这里,我们在 componentDidMount 函数中进行 API 调用,以确保它在组件加载后立即运行。 只有经过以下步骤,我们才能理解我们的数据:
- 首先,我们向 API 发出请求。
- 然后我们会收到回复。
- 我们从响应中提取数据。
- 最后,我们将数据存储在本地状态中。
如果在数据获取过程中发生错误:
- 我们发现了错误。
- 然后我们将数据存储在本地状态中。
在这里,我们明确描述了在获取数据的每个步骤中如何以及做什么。 尽管语法和函数可以正常工作,但如果以声明方式编写代码,则可以重写代码以提高效率并减少行数。 让我们使用 React-Async 重写它。
首先,您需要通过在终端中键入 npm install react-async
来安装该软件包。 然后使用以下代码编写您的组件:
import React, { Component } from 'react'; import Async from 'react-async'; const loadJson = () => fetch("https://api.coinmarketcap.com/v1/ticker/?limit=1") .then(res => (res.ok ? res : Promise.reject(res))) .then(res => res.json()) const App = () => ( <Async promiseFn={loadJson}> {({ data, error, isLoading }) => { if (isLoading) return "Loading..." if (error) return ``Something went wrong: ${error.message}`` if (data) return ( <div> {data.map(el => ( <li> {el.name}: {el.price_usd} </li> ))} </div> ) return null }} </Async> ) export default App;
如果您使用的是 CodeSandbox,请从依赖项菜单中添加 React-Async。 如果您想实时查看此代码,请查看 CodeSandbox 中的 React-Async 示例。
在这里,我们使用钩子而不是类重写了我们的组件。 我们首先创建一个函数 loadJson
来处理我们的数据获取。 然后,在我们的 App 组件中,我们使用通过 React-Async 库提供的 Async
组件。
一旦我们的承诺得到解决,我们就可以使用道具来处理不同的场景。
isLoading
可用,因此我们可以在尚未加载数据时显示用户友好的消息。error
在获取过程中出现错误时可用。data
是抓取完成后返回的实际数据。
在这个例子中,我们不再需要使用类或生命周期方法来加载我们的数据,也不需要告诉 React-Async 如何处理数据或如何更新我们的状态。
React-Async 通过 isLoading
后备属性管理加载状态,该属性会一直渲染,直到 data
准备好渲染,即当依赖的异步调用解析并返回数据时。
辅助组件
React-Async 附带了几个 辅助组件 ,它们使您的 JSX 更具声明性和更少混乱。 每个辅助组件只会在适当的时候渲染它的子组件。 我们可以重写我们的 App 函数,如下所示:
const App = () => ( <Async promiseFn={loadJson}> <Async.Loading>Loading...</Async.Loading> <Async.Resolved> {data => ( <div> {data.map(el => ( <li> {el.name}: {el.price_usd} </li> ))} </div> )} </Async.Resolved> <Async.Rejected> {error => `Something went wrong: ${error.message}`} </Async.Rejected> </Async> )
在 CodeSandbox 上查看此示例。
在此示例中,我们使用了 Async.Loading
、Async.Resolved
和 Async.Rejected
函数来简化我们的代码并使其更具可读性。 React-Async 提供的辅助组件可以将 React 元素或函数作为子元素。 当您提供一个函数时,您将收到可以在组件中使用的渲染道具。
具有辅助功能的用户配置文件测试应用程序
让我们构建一个使用更多帮助函数的小型用户配置文件应用程序。 将您的组件更新为以下代码:
import React, { Component } from 'react'; import Async from 'react-async'; const loadUser = ({ userId }) => fetch('`https://reqres.in/api/users/${userId}'`) .then(res => (res.ok ? res : Promise.reject(res))) .then(res => res.json()) const UserPlaceholder = () => ( <div> <div>User Details Loading</div> </div> ) const UserDetails = ({ data }) => ( <div className="details"> <img className="avatar" src={data.data.avatar} alt="" /> <div> {data.data.first_name} {data.data.last_name} </div> </div> ) const App = () => ( <Async promiseFn={loadUser} userId={1}> <Async.Pending> <UserPlaceholder /> </Async.Pending> <Async.Fulfilled>{data => <UserDetails data={data} />}</Async.Fulfilled> <Async.Rejected>{error => <p>{error.message}</p>}</Async.Rejected> </Async> ) export default App;
让我们回顾一下我们声明的函数:
loadUser
:我们定义这个函数来处理数据获取。 它接受一个 prop (userId
) 并根据 id 查询 API。userPlaceholder
:这个是当promise还没有被resolve的时候,也就是数据还没有完成加载的时候会显示的fallback组件。userDetails
:该组件处理用户数据的实际显示。 它通过 props 获取数据,并且仅在 promise 解决后才呈现。Async.Pending
、Async.Fulfilled
和Async.Rejected
:这些函数可以简化我们的代码并使其更具可读性。
结论
在本教程中,我们探讨了如何使用 React-Async 库来帮助我们以声明方式获取数据。 我们还查看了它提供的一些辅助函数。 要了解有关 React-Async 的更多信息,请查看 GitHub 上的 React-Async 文档。