[React Query] 理解ReactQuery中的状态

在学习 ReactQuery 之前我觉得很有必要去理解 ReactQuery 中和缓存相关的各种状态,只有当你理解了这些状态你才能明白🫡ReactQuery 中很多 API 或者 Options 的用意

什么是 ReactQuery?

在回答这个问题之前有必要先来了解客户端有哪些状态,在客户端中状态其实主要分为两种:一种是和用户相关的设置它们通常存储在客户端,称为客户端状态 ;另一种则是来自服务器的称为服务器状态

ReactQuery 之前我们大多情况下没有对这两者进行区分,都是将它们视为 React 中的状态进行渲染,如果需要复用服务器状态我们通常会将它们存储在全局状态管理库中手动进行管理。

ReactQuery 出来则改变了这一点,ReactQuery 专注于 服务器状态 的管理,提供了请求状态管理(loadingerrorsuccess)、缓存、重试、轮询、预加载、后台更新数据等一系列功能,极大的简化了和服务器交互的代码

正因为 ReactQuery 提供很多复杂的功能,必然需要一些状态才能描述查询的完整过程, 下面我们来认识一下 ReactQuery 中和查询相关的一些状态

loading&error&success

涉及到请求我们通常都会维护 isLoadingisError 等状态来在不同阶段渲染不同的内容,如下面这段代码:

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

function UserInfo() {
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);
  const [userInfo, setUserInfo] = useState();
  const fetchUserInfo = () => {
    setIsLoading(true);
    setIsError(false);
    fetch("https://xxxx/user")
      .then((res) => {
        setUserInfo(res.data);
      })
      .catch((err) => {
        setIsError(true);
      })
      .finally(() => {
        setIsLoading(false);
      });
  };
  useEffect(() => {
    fetchUserInfo();
  }, []);

  return <div>{isLoading ? <p>Loading...</p> : <Info {...userInfo} />}</div>;
}

export default UserInfo;

ReactQuery 也不例外在 useQuery 中返回了 isLoadingisErrorisSuccessdata 等状态描述了请求的成功与否

js 复制代码
import React, { useEffect, useState } from "react";
import { useQuery } from "react-query";

function UserInfo({ id }) {
  const fetchUserInfo = () => {
    setIsLoading(true);
    setIsError(false);
    fetch(`https://xxxx/user/${id}`)
      .then((res) => {
        setUserInfo(res.data);
      })
      .catch((err) => {
        setIsError(true);
      })
      .finally(() => {
        setIsLoading(false);
      });
  };
  const {
    data: userInfo,
    isError,
    isLoading,
  } = useQuery({
    queryFn: fetchUserInfo,
  });
  return <div>{isLoading ? <p>Loading...</p> : <Info {...userInfo} />}</div>;
}

export default UserInfo;

isLoading 仅仅是在初始数据加载或者 useQuery 的 queryKey 发生变化重新查询时才会是 true

fetching&idle&paused

ReactQuery 通过 isLoadingisSuccessisError 对初始化请求进行描述,但是 ReactQuery 具备重试缓存后台更新数据等功能上面的三种状态就没办法充分描述了

因此 ReactQuery 还提供了 fetchStatus,这个 fetchStatus 有三个可能的值:

  • idle:空闲状态,此时不会发起请求(有可能是缓存仍生效不需要发起请求)
  • fetching: 数据正在==获取==中 (有可能是初次发起获取,也有可能是数据过期了在后台获取数据,总之对于一个查询来说只要数据在获取就是 fetching 状态)
  • paused:数据暂停获取(对于数据获取情况总是多变的有可能有某些异常原因导致请求没有正常发起,此时查询 query 的状态就会变成 paused;等到⌛️可以发起数据了 ReactQuery 会发起请求,状态会重新变成 fetching )\

stale&fresh

ReactQuery 最重要的能力莫过于对缓存 的管理,怎么知道数据是新鲜的还是过期了呢?ReactQuery 中是通过 stale(过期)、fresh(新鲜)来进行描述

在默认情况下 useQuery 在组件挂载完成✅初次获取数据时会立刻变成过期状态,如果你想改变这一点就需要用到 staleTime 它可以设置缓存过期的时间,当数据超过定义的缓存有效时间就被标记为过期 stale 的,下次查询的时候会触发数据的更新和获取

js 复制代码
const userInfoQuery = useQuery({
	queryKey: ["userInfo"],
	queryFn: () => fetchUserInfo(id),
	staleTime: 1000 * 5,
});

说到 staleTime 就不得不提一嘴 cacheTime,它们两有什么区别呢?

  • staleTime:定义的是缓存的过期时间,类似于浏览器的 max-age,缓存过期了 ReactQuery 会讲缓存标记为过期的
  • cacheTime:定义的是缓存的==存储==时间,当 cacheTime 过期了,缓存就会被 ReactQuery 清空

active&inactive

对于查询本身也有活跃的(active)、不活跃的(inactive),如何理解活跃与不活跃呢?

当组件被挂载,使用到的 useQuery 查询会变成活跃状态,当组件被卸载,这个组件使用到的 useQuery 查询会变成非活跃状态,为了更直观的感受下面有一个小例子:

可以看到当我们将组件进行隐藏后组件卸载,随即用户信息组件中的查询就变成了 inactive 非活跃状态了

DEMO 地址:codesandbox.io/p/sandbox/r...

什么时候会触发更新

有了对上面对 Query 的各种状态的基本了解之后我们就可以来看一下,什么时候会触发查询的数据更新

  • 默认 ReactQuery 会在网络重新连接时会触发过期 stale 状态的查询数据获取,可以通过 refetchOnReconnect 这个查询配置进行修改
  • 默认在浏览器窗口 Window Focus 时会触发对 stale 过期状态的查询的数据获取
  • 默认会在组件 Mount(挂载) 时,触发 stale 过期的查询的数据获取
  • 除了上面的 ReactQuery 帮我们触发数据获取,ReactQuery 也提供了 queryClient.refetchQueriesqueryClient.invalidateQueries 给我们去触发数据的获取
js 复制代码
export default function Detail() {
  // 注意这里拿到的id是string
  const [isShow, setIsShow] = useState(true);
  const queryClient = useQueryClient();

  const refetchQueries = () => {
    queryClient.refetchQueries({
      queryKey: ["userInfo"],
    });
  };

  const invalidateQueries = () => {
    queryClient.invalidateQueries({
      queryKey: ["userInfo"],
    });
  };

  return (
    <div>
      <button onClick={() => setIsShow((isShow) => !isShow)}>
        {isShow ? "隐藏用户信息" : "展示用户信息"}
      </button>
      {isShow ? <UserInfo /> : null}
      <button onClick={refetchQueries}>refetchQueries</button>
      <button onClick={invalidateQueries}>invalidateQueries</button>
    </div>
  );
}

!tip

注意 queryClient.invalidateQueries 只会触发 active 活跃状态的查询,而 queryClient.refetchQueries 无论查询是否处于活跃状态都会触发数据重新获取

说到 staleTime 就不得不提一嘴 cacheTime,它们两有什么区别呢?

  • staleTime:定义的是缓存的过期时间,类似于浏览器的 max-age,缓存过期了 ReactQuery 会讲缓存标记为过期的
  • cacheTime:定义的是缓存的==存储==时间,当 cacheTime 过期了,缓存就会被 ReactQuery 清空

active&inactive

对于查询本身也有活跃的(active)、不活跃的(inactive),如何理解活跃与不活跃呢?

当组件被挂载,使用到的 useQuery 查询会变成活跃状态,当组件被卸载,这个组件使用到的 useQuery 查询会变成非活跃状态,为了更直观的感受下面有一个小例子:

可以看到当我们将组件进行隐藏后组件卸载,随即用户信息组件中的查询就变成了 inactive 非活跃状态了

DEMO 地址:codesandbox.io/p/sandbox/r...

什么时候会触发更新

有了对上面对 Query 的各种状态的基本了解之后我们就可以来看一下,什么时候会触发查询的数据更新

  • 默认 ReactQuery 会在网络重新连接时会触发过期 stale 状态的查询数据获取,可以通过 refetchOnReconnect 这个查询配置进行修改
  • 默认在浏览器窗口 Window Focus 时会触发对 stale 过期状态的查询的数据获取
  • 默认会在组件 Mount(挂载)时,触发 stale 过期状态的查询的数据获取
  • 除了上面的 ReactQuery 帮我们触发数据获取,ReactQuery 也提供了 queryClient.refetchQueriesqueryClient.invalidateQueries 给我们去触发数据的获取
js 复制代码
export default function Detail() {
  // 注意这里拿到的id是string
  const [isShow, setIsShow] = useState(true);
  const queryClient = useQueryClient();

  const refetchQueries = () => {
    queryClient.refetchQueries({
      queryKey: ["userInfo"],
    });
  };

  const invalidateQueries = () => {
    queryClient.invalidateQueries({
      queryKey: ["userInfo"],
    });
  };

  return (
    <div>
      <button onClick={() => setIsShow((isShow) => !isShow)}>
        {isShow ? "隐藏用户信息" : "展示用户信息"}
      </button>
      {isShow ? <UserInfo /> : null}
      <button onClick={refetchQueries}>refetchQueries</button>
      <button onClick={invalidateQueries}>invalidateQueries</button>
    </div>
  );
}

注意 queryClient.invalidateQueries 只会触发 active 活跃状态的查询,而 queryClient.refetchQueries 无论查询是否处于活跃状态都会触发数据重新获取

相关推荐
牛仔只喝牛奶12 小时前
多端跨端技术选型:小程序 + H5 + App 的两栈拆分落地
react native·react.js
wear工程师15 小时前
setState 到底是不是异步的?这题从 React 16 问到 18,标准答案早就变了
react.js·面试
wanghowie17 小时前
LangGraph4j 落地(二)— ReAct 多步工具与 trace 扩展
前端·react.js·前端框架
csj5018 小时前
前端基础之《React(11)—state声明式》
前端·react.js
张元清19 小时前
React useLocalStorage Hook:SSR 安全的持久化状态(2026)
javascript·react.js
析数塔19 小时前
2026前端框架深度解析:React Compiler、Angular Zoneless、Vue 3.5重写游戏规则
前端·vue.js·react.js
杖雍皓19 小时前
没有服务器如何构建现代 Web 应用:平台选型与组合策略
运维·服务器·前端·react.js·github·netlify·vercel
sun_weitao19 小时前
微前端框架之qiankun 怎么用?vue3+vite+qiankun 和 react + qiankun
前端·react.js·前端框架
名字还没想好☜2 天前
React useEffect 依赖数组踩坑:闭包陷阱与清理函数
前端·javascript·react.js·react·useeffect
我是大卫2 天前
【图】React源码解析-第四部分:事件与跨层传递
react.js·源码