前言
如何通过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. 安装依赖
npm install react-query
2. 配置全局Provider
为了让 React Query 在你的整个应用中都能工作,需要在应用的根组件设置一个**QueryClientProvider
** ,React Query 中的所有 hook 运行时需要这个全局的上下文状态。
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
。
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.messagereturn (<ul>{data.map(todo => (<li key={todo.id}>{todo.title}</li>))}</ul>)
}function App() {return (<QueryClientProvider client={queryClient}><Todos /></QueryClientProvider>)
}export default App
useQuery
参数详细说明
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
**方便地用于实现分页查询。
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><buttononClick={() => setPage(old => Math.max(old - 1, 0))}disabled={page === 0}>Previous Page</button>{' '}<buttononClick={() => { 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 来应对各种复杂的业务场景,分页查询、无限加载、并发请求、接口预加载等等场景都能够完美处理。
更多的业务场景实战,我会在后续的文章中详细介绍,感兴趣的朋友可以关注一下。