React Query:React 中优雅简单的接口数据状态管理
前言
如何通过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. 安装依赖
typescriptnpm install react-query
2. 配置全局Provider
为了让 React Query 在你的整个应用中都能工作,需要在应用的根组件设置一个**QueryClientProvider
** ,React Query 中的所有 hook 运行时需要这个全局的上下文状态。
typescriptimport { 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
。
typescriptimport 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
参数详细说明
typescriptuseQuery(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
**方便地用于实现分页查询。
typescriptimport 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 来应对各种复杂的业务场景,分页查询、无限加载、并发请求、接口预加载等等场景都能够完美处理。
更多的业务场景实战,我会在后续的文章中详细介绍,感兴趣的朋友可以关注一下。