【React Query】终极教程02:初见 RQ

上一篇,深入理解了 React 中的状态,这篇就来开启 React Query 的大门!


React Query 能干嘛?

RQ 能干的事儿多着呢 ~

而且,React Query 是用 TypeScript 写的,也可以与 React Native 结合使用,开箱即用,基本不需要任何配置,主要支持以下的一些 Features:

  • 缓存:每次查询后,数据将在可配置的时间内缓存,并可在整个应用程序中重复使用。
  • 取消查询:可以取消查询,并在取消后执行操作。
  • 乐观更新:在突变时,可以轻松更新状态,从而为用户提供更好的用户体验。如果突变失败,还可以轻松恢复到之前的状态。
  • 并行查询:如果需要同时执行一个或多个查询,可以毫无压力地执行,也不会影响缓存。
  • 依赖查询 :有时,我们需要在另一个查询完成后再执行另一个查询。React Query 可让这一切变得简单,并避免嵌套 Promise
  • 分页查询:有了 React Query,这种用户界面模式变得更加简单。你会发现,使用分页 API、更改页面和渲染获取的数据都非常简单。
  • 无限查询:React Query 使另一种用户界面模式变得更加简单。你可以在用户界面中实现无限滚动,相信 React Query 会让你在获取数据时更加轻松。
  • 滚动恢复:你是否有过这样的经历:从一个页面导航出去,当导航回来时,发现页面已经滚动到了导航之前的位置?这就是滚动恢复,只要缓存了查询结果,它就能正常工作。
  • 数据重新获取:需要触发数据重新获取?通过 React Query,你只需编写一行代码就能做到这一点。
  • 数据预取:有时,你可以提前确定用户的需求和下一步操作。在这种情况下,你可以相信 React Query 可以帮助你提前预取数据并为你缓存。这样,你的用户体验就会得到改善,你的用户也会更开心。
  • 跟踪网络模式和离线支持:你是否遇到过用户在使用你的应用程序时失去网络连接的情况?不用担心,因为 React Query 可以跟踪网络的当前状态,如果因为用户失去连接而导致查询失败,那么一旦网络恢复,查询就会重试。

是不是觉得很神奇?有了 React Query,这些日常需要大量第三方库支持和封装的功能一把给你梭哈了,可以大大减少模板代码量,提升代码的易读性,让你的页面如丝般顺滑。

使用 React Query

本系列将使用 React18.x + TypeScript 来学习 React Query,并且使用 vite 新建项目:

yaml 复制代码
pnpm create vite
# 然后按步骤选择 React + TypeScript 即可

安装 React Query:

yaml 复制代码
npm i @tanstack/react-query
# or
yarn add @tanstack/react-query
# or
pnpm add @tanstack/react-query

React Query 的配置非常简单。要将 React Query 集成到应用程序中,只需要两个东西:

  • QueryClient
  • QueryClientProvider

QueryClient

在 React Query 中,有两种机制用于处理这种缓存,分别称为 QueryCacheMutationCache

  • QueryCache:负责存储与查询相关的所有数据。这些数据可以是查询的数据,也可以是查询的当前状态。

  • MutationCache:负责存储与突变相关的所有数据。这可以是突变的数据,也可以是其当前状态。

在使用 React Query 时,要做的第一件事就是创建一个 QueryClient 实例:

jsx 复制代码
import { QueryClient } from '@tanstack/react-query'
const queryClient = new QueryClient()

QueryClient 中有很多配置,不传的时候,框架会适使用默认值。可以传递四个选项作为参数。它们如下:

  • queryCache:全局使用的查询缓存。
  • mutationCache:全局使用的突变缓存。
  • logger:日志记录器,用于显示错误、警告和有用的调试信息。未指定时,React Query 将使用控制台对象。
  • defaultOptions:默认选项,所有查询和突变在整个应用程序中使用的默认选项。

QueryCache

我们可以手动配置 QueryCacheMutationCache ,所有查询和突变在出现错误(onError)或执行成功(onSuccess)时都可以执行一些逻辑,也可以在突变执行前(onMutate)执行一些逻辑。

在 QueryCache 中可以这样做:

jsx 复制代码
import { QueryCache } from '@tanstack/react-query'

const queryCache = new QueryCache({
  onError: error => {
    // 处理 error 的情况
  },
  onSuccess: data => {
    // 处理 success 的情况
  }
})

MutationCache

MutationCache 非常相似:

jsx 复制代码
import { MutationCache } from '@tanstack/react-query'

const mutationCache = new MutationCache({
  onError: error => {
    // 处理 error 的情况
  },
  onSuccess: data => {
    // 处理 success 的情况
  },
  onMutate: newData => {
    // 处理突变之前的情况
  },
})

默认情况下,可能都不会处理全局的,一般是在实例化缓存对象时,在相应对象的相应函数中进行配置。

在配置好后,将该对象发送给 QueryClient,就可以实例化了一个新的 QueryClient:

jsx 复制代码
const queryClient = new QueryClient({
   mutationCache,
   queryCache
})

Logger

如果你希望在项目中,除了使用控制台对象之外,配置日志记录器,那么你需要在 QueryClient 中进行配置:

js 复制代码
const logger = {
   log: (...args) => {
      // 调用你自定义的日志函数
   },
   warn: (...args) => {
      // 调用你自定义的warn日志函数
   },
   error: (...args) => {
      // 调用你自定义的error日志函数
   },
 };

然后,将此日志记录器传递给 QueryClient

jsx 复制代码
const queryClient = new QueryClient({
   // ...
   logger
})

defaultOptions

defaultOptions 允许你覆盖这些默认值,默认值有很多,这里就不展示了,官方网站有。

以下是覆盖 defaultOptions 的方法:

jsx 复制代码
const defaultOptions = {
   queries: {
     staleTime: Infinity,
   },
 };

在这个 queries 对象中,我们指定所有查询的 staleTime 为 Infinity。设置完成后,所有查询的 staleTime 属性都将设置为 Infinity

jsx 复制代码
const queryClient = new QueryClient({
   // ...
   defaultOptions
})

QueryClientProvider

React Query 基于 React Context,创建了 QueryClientProvider 的自定义 Provider,这样就可以在被它包裹的组件中使用 React Query 相关的 API 了。

在项目中引入:

jsx 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import CountProvider from "./components/CountProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const root = ReactDOM.createRoot(document.getElementById("root"));

const queryClient = new QueryClient();

root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <CountProvider>
        <App />
      </CountProvider>
    </QueryClientProvider>
  </React.StrictMode>
);

React Query Devtools

React Query 还提供了专门的开发工具 React Query Devtools,可以可视化地查看所有查询和突变的当前状态。安装:

yaml 复制代码
npm i @tanstack/react-query-devtools
# or
yarn add @tanstack/react-query-devtools
# or
pnpm add @tanstack/react-query-devtools

它有两种模式:浮动模式(Floating Mode)嵌入模式(Embedded Mode)

浮动模式

浮动模式将使 React Query 徽标浮动在屏幕一角。显示在屏幕角落的徽标如下:

打开后,你就会看到 Devtools:

Devtools 实际上会在 DOM 树中生成一个独立于 App 之外的 DOM 节点:

要在应用程序中添加浮动模式 Devtools,你需要导入它:

jsx 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import CountProvider from "./components/CountProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

const root = ReactDOM.createRoot(document.getElementById("root"));

const queryClient = new QueryClient();

root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <ReactQueryDevtools initialIsOpen={false} />
      <CountProvider>
        <App />
      </CountProvider>
    </QueryClientProvider>
  </React.StrictMode>
);

嵌入模式

嵌入式模式会将 Devtools 嵌入式作为常规组件添加到页面中,挤占页面的空间:

引入:

jsx 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import CountProvider from "./components/CountProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtoolsPanel } from "@tanstack/react-query-devtools";

const root = ReactDOM.createRoot(document.getElementById("root"));

const queryClient = new QueryClient();

root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <ReactQueryDevtoolsPanel />
      <CountProvider>
        <App />
      </CountProvider>
    </QueryClientProvider>
  </React.StrictMode>
);

你会看到:

通常来说,生产中不应该显示 Devtools。不过,你也可以在生产环境中加载它们,来调试代码。

在生产环境中使用 Devtools

如果你想在生产环境中加载 Devtools,则必须使用 延时加载,而不是用动态加载,这对于包体积的控制非常重要。懒加载 Devtools 也很重要,因为在生产环境中,我们可能永远都不会用到它,所以我们要避免在构建过程中添加我们根本用不上的东西。

要在 React 中懒加载组件,我们可以使用 React.lazy 来做:

jsx 复制代码
const ReactQueryDevtoolsProduction = React.lazy(() =>
  import('@tanstack/react-query-devtools/build/lib/index.prod.js').then(
    (d) => ({
      default: d.ReactQueryDevtools,
    }),
  ),
)

这样我们就可以在生产环境中懒加载它,而不会增加包的大小。

什么是动态导入?动态导入允许你在代码中任意位置异步加载模块,将返回一个Promise,成功后将返回一个包含模块导出的对象。

也可以像这样动态导入模块:

jsx 复制代码
const ReactQueryDevtoolsProduction = React.lazy(() =>
  import('@tanstack/react-query-devtools/production').then(
    (d) => ({
      default: d.ReactQueryDevtools,
    }),
  ),
)

在使用 React.lazy 并尝试渲染我们刚刚懒加载的组件时,必须要使用 Suspense 进行包裹,这样就会在懒加载的组件 loading 时显示 fallback 组件,进而提升用户体验。

jsx 复制代码
import React, { Suspense } from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import CountProvider from "./components/CountProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const root = ReactDOM.createRoot(document.getElementById("root"));

const queryClient = new QueryClient();

const ReactQueryDevtoolsProduction = React.lazy(() =>
  import("@tanstack/react-query-devtools/build/lib/index.prod.js").then(
    (d) => ({
      default: d.ReactQueryDevtools,
    })
  )
);

root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <Suspense fallback={null}>
        <ReactQueryDevtoolsProduction />
      </Suspense>
      <CountProvider>
        <App />
      </CountProvider>
    </QueryClientProvider>
  </React.StrictMode>
);

显然,我们不希望在渲染组件时自动加载 Devtools。那就还需要的是一个切换它的方法。由于这是一个生产构建,我们不希望在其中包含一个可能会让用户感到困惑的按钮。一种方法是在 window 对象中天假一个 toggleDevtools 函数。

React Query 文档建议我们这样做:

jsx 复制代码
import React, { Suspense, useEffect, useState } from "react";

const ReactQueryDevtoolsProduction = React.lazy(() =>
  import("@tanstack/react-query-devtools/build/lib/index.prod.js").then(
    (d) => ({
      default: d.ReactQueryDevtools,
    })
  )
);

const ReactQueryDevtools = () => {
  const [showDevtools, setShowDevtools] = useState(false);

  useEffect(() => {
    window.toggleDevtools = () =>
      setShowDevtools((previousState) => !previousState);
  }, []);

  return (
    showDevtools && (
      <Suspense fallback={null}>
        <ReactQueryDevtoolsProduction />
      </Suspense>
    )
  );
};

export default ReactQueryDevtools;

然后在 index.js 中使用:

js 复制代码
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import CountProvider from "./components/CountProvider";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import ReactQueryDevtools from "./components/ReactQueryDevToools";

const root = ReactDOM.createRoot(document.getElementById("root"));

const queryClient = new QueryClient();

root.render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <ReactQueryDevtools />
      <CountProvider>
        <App />
      </CountProvider>
    </QueryClientProvider>
  </React.StrictMode>
);

这样,我们就可以通过控制台来切换 devtools,像这样:


相关推荐
m0_7482517212 分钟前
DataOps驱动数据集成创新:Apache DolphinScheduler & SeaTunnel on Amazon Web Services
前端·apache
珊珊来吃13 分钟前
EXCEL中给某一列数据加上双引号
java·前端·excel
胡西风_foxww40 分钟前
【ES6复习笔记】Spread 扩展运算符(8)
前端·笔记·es6·扩展·运算符·spread
小林爱1 小时前
【Compose multiplatform教程08】【组件】Text组件
android·java·前端·ui·前端框架·kotlin·android studio
跨境商城搭建开发1 小时前
一个服务器可以搭建几个网站?搭建一个网站的流程介绍
运维·服务器·前端·vue.js·mysql·npm·php
hhzz1 小时前
vue前端项目中实现电子签名功能(附完整源码)
前端·javascript·vue.js
秋雨凉人心1 小时前
上传npm包加强
开发语言·前端·javascript·webpack·npm·node.js
时清云2 小时前
【算法】 课程表
前端·算法·面试
NoneCoder2 小时前
CSS系列(37)-- Overscroll Behavior详解
前端·css
Nejosi_念旧2 小时前
使用Webpack构建NPM Library
前端·webpack·npm