前言
如何通过Ajax或者Fetch优雅的请求后端接口,这是所有复杂前端项目都需要考虑处理的事情。在React项目中,有不少成熟的Hook能够让开发者管理整个请求过程中的数据和状态,例如 axios-hooks
、use-http
、react-query
、swr
甚至 ahook
中提供的 useRequest
。
我曾经很长一段时间是直接使用 ahook
中的 useRequest
,但是有的项目中不需要 ahook
中的其他hook,我又不愿意仅仅为了使用 useRequest
而在项目中引入 ahook
。就像我第参与的一个前端项目中仅仅为了使用 jquery
的 $.ajax
而引入 jquery
,至今我都在怀疑当时前端负责人的真实水平。
最开始听说 react-query
时,我还以为它是处理URL 字符串的工具库,随着慢慢熟练使用 react-query
后发现是真的很好用。本文将详细介绍 react-query
的功能、优势以及如何使用。
React Query 介绍
React Query 是一个与数据无关的、强大的数据同步库。它通过提供一些 Hooks,可以帮助我们以简单的方式同步后端数据到我们的 React 组件中。
React Query 对于数据获取、同步、缓存、更新等操作提供了极好的解决方案。
解决问题
-
网络请求的缓存和更新
React Query 自动处理缓存、数据的更新和废弃,不需要手动操作,简化操作流程。
-
后台更新
React Query 可以在后台静默更新数据,不影响用户的使用体验。
-
调试工具
React Query Devtools 提供了全面的调试支持,使数据流的控制和错误的追踪更为容易。
使用步骤
1. 安装依赖
typescript
npm install react-query
2. 配置全局Provider
为了让 React Query 在你的整个应用中都能工作,需要在应用的根组件设置一个**QueryClientProvider
** ,React Query 中的所有 hook 运行时需要这个全局的上下文状态。
typescript
import { QueryClient, QueryClientProvider } from 'react-query'
import Todos from './Todos'
const queryClient = new QueryClient()
function App() {
return (
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
export default App
3. 简单的 GET 请求
使用React Query 的主要获取数据的 Hook useQuery
,useQuery
是最基础的数据获取 Hook。 可以获取、缓存、同步、自动更新数据。
下面的示例中,它会请求并显示所有的 todos
。
typescript
import React from 'react'
import { QueryClient, QueryClientProvider, useQuery } from 'react-query'
import axios from 'axios'
const queryClient = new QueryClient()
function Todos() {
const fetchTodos = async () => {
const { data } = await axios.get('/api/todos')
return data
}
**const { isLoading, error, data } = useQuery('todos', fetchTodos)**
if (isLoading) return 'Loading...'
if (error) return 'An error has occurred: ' + error.message
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
function App() {
return (
<QueryClientProvider client={queryClient}>
<Todos />
</QueryClientProvider>
)
}
export default App
useQuery
参数详细说明
typescript
useQuery(queryKey, queryFn, options)
参数 | 详细参数 | 可选类型 | 作用说明 |
---|---|---|---|
queryKey | **String | Array** | |
queryFn | Function | 这个函数会返回一个 Promise,用于获取数据。如果你的 query key 是一个数组,每个数组的元素都会作为单独的参数传递给这个函数。 | |
options | cacheTime | Number | 不活动的数据在被自动垃圾回收清除之前的毫秒数。默认是 5 分钟。 |
staleTime | Number | 数据在获取后变得过时之前的毫秒数。默认情况下,数据在获取后就会立即变过时。 | |
retry | Number | 如果查询失败,将尝试重新查询的次数。默认是 3 次。 | |
retryDelay | Number | 重试查询之间的毫秒数。默认为 1000 毫秒。 |
进阶用法
1. 如何实现分页查询
React Query提供了**usePaginatedQuery
**方便地用于实现分页查询。
typescript
import React from 'react'
import { usePaginatedQuery } from 'react-query'
import axios from 'axios'
const fetchProjects = async (key, page = 0) => {
const res = await axios.get(`/api/projects?page=${page}`)
return res.data
}
function Projects() {
const [page, setPage] = React.useState(0)
const {
isLoading,
isError,
error,
resolvedData,
latestData,
isFetching,
} = usePaginatedQuery(['projects', page], fetchProjects)
if (isLoading) return 'Loading...'
if (isError) return `An error has occurred: ${error.message}`
return (
<div>
{resolvedData.projects.map(project => (
<p key={project.id}>{project.name}</p>
))}
<div>
<button
onClick={() => setPage(old => Math.max(old - 1, 0))}
disabled={page === 0}
>
Previous Page
</button>{' '}
<button
onClick={() => {
if (!latestData || !latestData.hasMore) {
return
}
setPage(old => old + 1)
}}
disabled={!latestData || !latestData.hasMore}
>
Next Page
</button>
</div>
{isFetching ? ' Loading more...' : null}
</div>
)
}
export default Projects
需要注意的点
**resolvedData
是上次成功获取的数据,而 latestData
则是最新尝试过获取的数据,所以当用户点击"下一页"按钮时,新的数据正在加载,但在新的数据被成功加载之前, resolvedData
**仍然是上次成功获取的数据。
总结
如果你的项目技术栈使用的是React、并且你的项目需要与后端进行接口交互,那么 React Query 是很好的提效工具。React Query 提供了十多个 Hook 来应对各种复杂的业务场景,分页查询、无限加载、并发请求、接口预加载等等场景都能够完美处理。
更多的业务场景实战,我会在后续的文章中详细介绍,感兴趣的朋友可以关注一下。