开始使用React-Query进行数据获取和状态管理
React.js是一个强大的客户端JavaScript库。像其他的JavaScript库一样,React.js在构建反应式和声明式用户界面时给你带来了流畅的体验。
然而,它也有一些负面的东西,比如状态管理和数据获取。
服务器状态管理与其他库有些不同。在React.js中,它是异步的,数据在远程持久化,没有直接控制。这意味着我们必须更新、缓存或重新获取数据,以有效地管理React.js应用程序中的状态。
React Query是一个预配置的库,旨在解决这些复杂问题。
使用React Query,我们可以在基于React的应用程序中以简单和声明的方式获取、缓存和更新数据,而不改变全局状态。
目标
如今,几乎每个网络应用都在使用远程数据。不幸的是,对于开发者来说,在React应用中获取数据和处理服务器状态说起来容易,做起来难。
作为开发者,我们需要重新思考。
- 在等待远程数据加载时要渲染什么?
- 如果发生错误会怎样?
- 我们如何让客户端与服务器保持同步?
- 这些权衡将如何影响互联网连接不佳的用户?
当处理需要频繁更新、缓存和与服务器同步的异步数据时,没有比React-Query更好的库了。
在本教程中,我们将了解React-Query库如何在我们的react应用中改善用户体验。
为了演示这些概念,我们将使用JSON Placeholder作为第三方REST API。
前提条件
对于本教程,我们需要具备以下条件。
- 对React钩子和功能组件有中级水平的技能是必不可少的。
- 对REST APIs和JavaScript中的数据获取有基本的了解。
- 确保你的机器上安装了Node.js运行时间。
了解状态管理和服务器状态
每个交互式客户端应用程序都会涉及到交互式事件。
例如,当用户通过点击按钮或关闭侧边栏与应用程序互动时,应用程序必须相应地重新渲染页面以反映这些变化。我们称这种变化为应用程序的状态。
在React.js和单页应用(SPA)的背景下,状态管理是一种在不同的React组件之间共享数据的方式。
换句话说,状态只是一个代表组件的一部分的JavaScript对象,它可以根据用户的操作而改变。
在react.js应用程序中使用React-Query的好处
React-Query提供的一些功能包括。
- 使用窗口焦点预取机制,根据应用程序标签活动预取数据。
- 我们可以为任何请求设置请求重试的次数,以备随机错误。
- React-Query执行预取,以便应用程序可以在后台更新陈旧的数据。
- 处理复杂的应用程序缓存,使请求操作得到优化。
安装和设置
为了演示这些概念,我们将实现一个获取数据的React Todo应用程序,从JSON占位符--一个假的REST API中获取帖子。
首先,让我们设置一个基本的React.js应用程序。
使用下面的命令,在react-query-demo 目录中创建一个模板式的React.js应用程序。
npx create-react-app react-query-demo
接下来,我们需要添加axios ,以进行HTTP请求和react-query 。
npm i react-query axios
对于yarn,执行。
yarn add react-query axios
接下来,使用这个命令启动应用程序。
npm start
这就触发了我们的开发服务器,在http://localhost:3000 的热重载。
为了在React.js应用程序中配置React-Query,我们需要用QueryClientProvider 组件来包装需要获取数据的组件。
QueryClientProvider 的子组件现在可以访问由React-Query库提供的钩子,该库为我们提供了一个QueryClient 实例。我们将使用这个实例来访问React-Query库所提供的钩子。
为了启动React-Query,我们将在根目录文件中使用一个基本配置 *index.js*作为。
import React from 'react'
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import { QueryClient, QueryClientProvider } from "react-query";
const queryClient = new QueryClient();
ReactDOM.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>,
document.getElementById('root')
);
在接下来的步骤中,我们将使用useQuery 、useMutation 、和本地React钩子来实现数据获取。
使用React-Query获取数据
React-Query和常见的数据获取模式(如useEffect )之间的区别是,React-Query将首先返回之前获取的数据,然后再次重新获取数据。
如果资源与第一次相同,React-Query将保留两个数据作为参考,而不强迫页面重新加载。
而useEffect ,不管修改后的数据如何,都会取回数据并重新加载页面。
为了演示这些概念,我们将使用Post 和Posts 组件。获取数据的逻辑将被写在Posts.js 。
继续,在src 应用程序文件夹中创建一个components 文件夹。
首先,导入React 、useQuery 钩子和axios 库。
import React from 'react'
import {useQuery} from 'react-query'
import axios from 'axios'
我们需要定义一个异步函数fetchPosts() ,它将向JSON占位符API发出网络请求以获取帖子。
async function fetchPosts(){
const {data} = await axios.get('https://jsonplaceholder.typicode.com/posts')
return data
}
最后,创建一个React功能组件。
function Posts(){
const {data, error, isError, isLoading } = useQuery('posts', fetchPosts)
// first argument is a string to cache and track the query result
if(isLoading){
return <div>Loading...</div>
}
if(isError){
return <div>Error! {error.message}</div>
}
return(
<div className='container'>
<h1>Posts</h1>
{
data.map((post, index) => {
return <li key={index}>{post.title}</li>
})
}
</div>
)
}
export default Posts
让我们简单地剖析一下上面的代码。
- 我们创建一个名为
Posts的React功能组件,它被导出为default。 useQuery钩子返回一个应用状态,可以是isLoading或isError。useQueryhook需要两个参数,第一个参数是一个字符串,用于缓存和跟踪查询结果。- 第二个参数是我们定义的进行HTTP请求的函数,
fetchPosts。react-query内部处理缓存和数据的更新。 - 在组件的返回语句之前,我们执行一个基本逻辑来检查应用程序是否处于加载状态或发生错误。
- 在JSX部分,我们通过帖子数组的映射来返回帖子。
为了获得出色的开发者体验,你可以使用
react-query-devtools。react-query包括内置的工具react-query-devtools,帮助我们查看状态和缓存。
要在你的应用程序中启用这一功能,import { ReactQueryDevtools } from 'react-query/devtools' 。
使用'useMutation'钩子做一个POST请求
与useQuery 钩子不同,突变是用来执行CREATE 、UPDATE 、或DELETE 操作的,作为我们组件内部的服务器端操作。
在我们的例子中,我们将使用useMutation ,执行一个POST 的请求。
在components 文件夹内,创建一个Post.js 文件。
在Post.js 组件文件中,导入axios,React 和useMutation 钩子,如下所示。
import React, {Fragment, useState} from 'react'
import { useMutation } from 'react-query'
import axios from 'axios'
接下来,创建一个Post 功能组件。
export default function Post(){
const [title, setTitle] = useState('')
const [description, setDescription] = useState('')
const [message, setMessage] = useState('')
const {isLoading, isError, error, mutate} = useMutation(createPost, {retry: 3})
async function createPost() {
const response = await axios.post('https://jsonplaceholder.typicode.com/posts')
setMessage(response.data)
}
return(
<Fragment>
<div className="post">
<h1>Create a Post</h1>
<label>Title:</label>
<input type="text" value={title} onChange={e=>setTitle(e.target.value)}/>
<label>Description:</label>
<input type="text" value={description} onChange={e=>setDescription(e.target.value)}/>
<button onClick={() => {mutate({ id: Date.now(), title, description })}} >Create</button>
<p> Created a new Post ID: {message && message.id}</p>
<div style={{color: 'gray', background: '#234'}}>
{isLoading
? "Saving...": ""
}
{
isError
? error.message : ""
}
</div>
</div>
</Fragment>
)
}
简要说明一下上面的代码。
createPost是一个函数,使用 库向第三方API发出HTTP POST请求。axiosuseMutation()钩子返回isLoading,isError,error, 和mutate函数,这些函数在发出请求时将被用来包装数值。- 它把
createPost作为一个参数,同时还有一个选项{retry:3}对象。这在突变后预取查询和确保乐观更新时很有用。 - 预取允许我们在需要数据之前对其进行取值操作。乐观更新的好处是,如果出现问题,我们可以选择回滚我们的更新。
useState()钩子被用来创建和更新输入元素的title,和description状态。- 在创建后的功能下面,我们使用
isLoading和isError来处理相应的突变状态。我们可以使用isLoading来表示一些东西正在被异步发布到服务器上。isError和error将在出现错误的情况下给我们提供信息。
上述具有获取和发布功能的代码应该是这样的。


因此,我们可以使用React-Query从第三方REST API获取和发布数据,而不需要重新加载屏幕。
总结
构建前端应用程序往往开始时很容易,但随着我们继续添加功能,就会变得很复杂。对于我们添加的每个API端点,我们还需要处理状态管理、同步、缓存和错误处理。
React-Query库帮助我们管理网络服务请求中涉及的数据,使我们的应用程序在复杂度增加时保持可维护性,同时改善用户体验。
React-Query经常被描述为React生态系统中缺失的部分,这个教程让我们了解了这个很棒的工具。