React Query 实战教程:在 React 中如何优雅的管理接口数据状态?

前言

如何通过Ajax或者Fetch优雅的请求后端接口,这是所有复杂前端项目都需要考虑处理的事情。在React项目中,有不少成熟的Hook能够让开发者管理整个请求过程中的数据和状态,例如 axios-hooksuse-httpreact-queryswr甚至 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 对于数据获取、同步、缓存、更新等操作提供了极好的解决方案。

解决问题

  1. 网络请求的缓存和更新

    React Query 自动处理缓存、数据的更新和废弃,不需要手动操作,简化操作流程。

  2. 后台更新

    React Query 可以在后台静默更新数据,不影响用户的使用体验。

  3. 调试工具

    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 useQueryuseQuery 是最基础的数据获取 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 来应对各种复杂的业务场景,分页查询、无限加载、并发请求、接口预加载等等场景都能够完美处理。

更多的业务场景实战,我会在后续的文章中详细介绍,感兴趣的朋友可以关注一下。

相关推荐
学不会•2 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o4 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic5 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā5 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年7 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder7 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_882727577 小时前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架