目的
也就是解决了什么问题?
常规流程
请求数据 =>加载中 => 后端返回 => 如果有报错展示报错 =>刷新数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | import * as React from "react";
  export default function App() {      const [zen, setZen] = React.useState("");    const [isLoading, setIsLoading] = React.useState(false);    const [isError, setIsError] = React.useState(false);    const [errorMessage, setErrorMessage] = React.useState("");
    const fetchData = () => {          setIsLoading(true);
      fetch("https://api.github.com/zen")       .then(async (response) => {                  if (response.status !== 200) {           const { message } = await response.json();
            throw new Error(message);         }
          return response.text();       })       .then((text: string) => {                  setIsLoading(false);          setIsError(false);          setZen(text);       })       .catch((error) => {                  setIsLoading(false);          setIsError(true);          setErrorMessage(error.message);       });   };
    React.useEffect(() => {          fetchData();   }, []);
    return (     <div>            <h1>Zen from Github</h1>     <p>         {isLoading ? "加载中..." : isError ? errorMessage : zen}       </p>   {" "}       {!isLoading && (         <button onClick={fetchData}>{isError ? "重试" : "刷新"}</button>       )}               </div>   ); }
   | 
 
- 使用 isLoading 来存储加载状态
 
- 使用 isError 来存储接口是否有错误
 
- 使用 errorMessage 来存储后端返回的报错信息
 
- 使用 zen 来存储后端返回数据存储
 
- 重新调用 fetchData 方法来刷新数据
 
修正
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
   | import * as React from "react"; import { useQuery } from "react-query";
  const fetchData = () => {   return fetch("https://api.github.com/zen").then(async (response) => {          if (response.status !== 200) {       const { message } = await response.json();
        throw new Error(message);     }
      return response.text();   }); };
  export default function App() {   const zenQuery = useQuery(["zen"], fetchData); 
    return (     <div>            <h1>Zen from Github</h1>     <p>              {" "}         {zenQuery.isLoading || zenQuery.isFetching           ? "加载中..."           : zenQuery.isError           ? zenQuery.error?.message           : data}                     </p>   {" "}       {!zenQuery.isLoading && !zenQuery.isFetching && (         <button           onClick={() => {             zenQuery.refetch();           }}         >                   {zenQuery.isError ? "重试" : "刷新"}                </button>       )}               </div>   ); }
   | 
 
结果
为了解决这么多大量冗杂的模板代码.使用 react-query 会变得十分整洁.
上面各种 loading,error,数据刷新等等都交给 react-query 处理.
初始配置
将组件包裹在QueryClientProvider中,和 useContext 搭配就是把 useContext 实例化的组件放在其中.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
   | import {   QueryClient,   QueryClientProvider,   useQuery, } from "@tanstack/react-query";
  const queryClient = new QueryClient();
  function Example() {   const query = useQuery("todos", fetchTodos);
    return (     <div>       {query.isLoading         ? "Loading..."         : query.isError         ? "Error!"         : query.data         ? query.data.map((todo) => <div key={todo.id}>{todo.title}</div>)         : null}     </div>   ); }
  function App() {   return (     <QueryClientProvider client={queryClient}>       <Example />     </QueryClientProvider>   ); }
  | 
 
搭配 useContext
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
   | import { ReactNode } from "react"; import { AuthProvider } from "./auth.context"; import { QueryClient, QueryClientProvider } from "react-query";
  export const AppProviders = ({ children }: { children: ReactNode }) => {   return (     <QueryClientProvider client={new QueryClient()}>       <AuthProvider>{children}</AuthProvider>     </QueryClientProvider>   ); };
 
  import * as auth from "auth-provider"; import { FullPageLoading } from "components/lib"; import { createContext, ReactNode, useContext } from "react"; import { useQueryClient } from "react-query"; import { User } from "types/user"; import { useMount } from "utils"; import { http } from "utils/http"; import { useAsync } from "utils/use-async"; import { FullPageErrorFallback } from "../components/lib";
  const AuthContext = createContext<   | {       user: User | null;       login: (form: AuthForm) => Promise<void>;       register: (form: AuthForm) => Promise<void>;       logout: () => Promise<void>;     }   | undefined >(undefined);
  AuthContext.displayName = "AuthContext";
  interface AuthForm {   username: string;   password: string; }
 
  const bootstrapUser = async () => {   let user = null;   const token = auth.getToken();   if (token) {     const data = await http("me", { token });     user = data.user;   }   return user; };
  export const AuthProvider = ({ children }: { children: ReactNode }) => {   
    const {     run,     isIdle,     isLoading,     isError,     error,     data: user,     setData: setUser,   } = useAsync<User | null>();   const queryClient = useQueryClient();
    const login = (form: AuthForm) => auth.login(form).then(setUser);   const register = (form: AuthForm) => auth.register(form).then(setUser);   const logout = () =>     auth.logout().then(() => {       setUser(null);              queryClient.clear();     });
       useMount(() => {     run(bootstrapUser());   });
    if (isIdle || isLoading) {     return <FullPageLoading />;   }   if (isError) {     return <FullPageErrorFallback error={error} />;   }
    return (     <AuthContext.Provider       children={children}       value={{ user, login, register, logout }}     />   ); };
  export const useAuth = () => {   const context = useContext(AuthContext);
    if (!context) {     throw new Error("useAuth必须在AuthProvider中使用");   }   return context; };
  | 
 
常用方法
查询键和查询函数
1
   | const zenQuery = useQuery(["zen"], fetchData);
   | 
 
- 其中
['zen']就是 react-query 的查询键,react-query 通过不同的查询键来标识(映射)不同接口(或是同一接口不同参数请求)返回的数据。在 react-query@4 中,查询键必须是数组。 
- 而
fetchData就是我们请求后端接口的函数,也就是查询函数.
PS:查询键内的元素可以是嵌套数组、对象、字符串、数字
例如:[‘zen’, { form: ‘confucius’ }]或[‘zen’, [‘confucius’, ‘Lao Tzu’]]
 
为了方便记忆,打个比方,你可以将查询键看做是你存储 localStorage 时的 key,而 value 则是通过查询函数查询到数据后,将各种我们需要的状态数据存储进入 value
使用变量作为查询键的元素时,当变量的值变化后,react-query 将会重新调用 fetchData 方法,获取新的数据,并缓存到对应变量值为 key 的缓存中。
并行请求
同时多个请求就写多个 useQuery,合并请求就用 Promise.all 包裹.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
   | import * as React from "react"; import { useQuery } from "react-query";
  const getReposAndGists = (username) => {   return Promise.all([     fetch(`https://api.github.com/users/${username}/repos`).then((res) =>       res.json()     ),     fetch(`https://api.github.com/users/${username}/gists`).then((res) =>       res.json()     ),   ]); };
  const ReposAndGists = ({ username }) => {   const reposAndGistsQuery = useQuery(["reposAndGists", username], () =>     getReposAndGists(username)   );
    if (reposAndGistsQuery.isLoading) {     return <p>加载数据中...</p>;   }
    if (reposAndGistsQuery.isError) {     return <p>数据加载错误: {reposAndGistsQuery.error.message}</p>;   }
    if (!reposAndGistsQuery.data) {     return null;   }
    const [repos, gists] = reposAndGistsQuery.data;
    return (     <div>       <h2>仓库列表</h2>       <ul>         {repos.map((repo) => (           <li key={repo.id}>{repo.name}</li>         ))}       </ul>
        <hr />
        <h2>代码片段列表</h2>       <ul>         {gists.map((gist) => (           <li key={gist.id}>{gist.description || "暂无描述"}</li>         ))}       </ul>     </div>   ); };
  export default ReposAndGists;
   | 
 
动态生成请求
useQueries.从动态获取[‘facebook’, ‘vuejs’, ‘nestjs’, ‘mongdb’],到重新批量获取了以下用户的仓库[‘microsoft’, ‘tesla’].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
   | import * as React from "react"; import { useQueries } from "react-query";
  export default function App() {   const [users, setUsers] = React.useState([     "facebook",     "vuejs",     "nestjs",     "mongodb",   ]);
    const getRepos = (username) =>     fetch(`https://api.github.com/users/${username}/repos`).then((res) =>       res.json()     );
    const userQueries = useQueries({     queries: users.map((user) => {       return {         queryKey: ["user", user],         queryFn: () => getRepos(user),       };     }),   });   return (     <div>            <h1>查看github用户的仓库</h1>     <button         onClick={() => setUsers(["microsoft", "tesla"])}       >                更改获取用户             </button>   {" "}       {userQueries.map((query) =>         query.isLoading ? (           <div>加载中....</div>         ) : (           <ol>                      {" "}             {query.data.map((item) => (               <li>{item.full_name}</li>             ))}                                 </ol>         )       )}               </div>   ); }
   | 
 
依赖请求
请求 B 接口的某个参数依赖 A 接口请求返回的内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
   | import * as React from "react"; import { useQuery } from "react-query";
  const IssueLabelFilter = ({ owner, repo }) => {   const labelsQuery = useQuery(["repos", owner, repo, "labels"], () =>     fetch(`https://api.github.com/repos/${owner}/${repo}/labels`).then((res) =>       res.json()     )   );
    const labels = labelsQuery.data;
    const issuesQuery = useQuery(     ["repos", owner, repo, "issues"],     () =>       fetch(         `https://api.github.com/repos/${owner}/${repo}/issues?labels=${labels[1].name}`       ).then((res) => res.json()),     {       
 
 
 
 
        enabled: !!labels,     }   );
    return (     <div>       <h2>标签</h2>       {labelsQuery.isLoading ? (         <p>加载标签中...</p>       ) : (         <ul>           {labelsQuery.data.map((label) => (             <li key={label.id}>{label.name}</li>           ))}         </ul>       )}
        <hr />
        <h2>         Issues         {Array.isArray(issuesQuery.data) ? `(${labels[1].name})` : ""}       </h2>       {issuesQuery.isLoading ? (         <p>加载issues中...</p>       ) : (         <ul>           {issuesQuery.data.map((issue) => (             <li key={issue.id}>{issue.title}</li>           ))}         </ul>       )}     </div>   ); };
  export default function App() {   return (     <div>       <IssueLabelFilter owner={"facebook"} repo={"react"} />     </div>   ); }
   | 
 
在 react-query 中,当该组件被加载时,组件内的 useQuery 就会开始请求。
此时明显不符合需求,需求的要求是在加载完标签列表后,获取到第二个标签,再开始请求 issues 列表。
因此就需要使用到 useQuery 的 enabled 参数,当参数值为 false 时,将会禁止请求接口。
现在回到上面的例子中,当 labelsQuery 请求还没有结果时,labels 变量值为 undefined,此时在 ① 行代码中的值为 false,当 labelsQuery 请求结束时,labels 变量值为数组,此时在 ① 行代码中的值为 true,issuesQuery 开始请求数据。完全符合需求的要求。
改进依赖查询接口一直为 loading 的问题
上面的例子中,issuesQuery 在加载后,由于处于禁用状态(配置项 enabled: false),此时 isLoading 将会一直处于 true 的状态,直到 issuesQuery 请求完成数据后变为 false。
这种提示会非常奇怪,明明有一段时间里 issuesQuery 没有真正的请求数据,为啥要一直显示加载标签中…的内容?
解决办法是:需要一个字段来区分查询函数当前并没有请求数据,处于摸鱼状态。
在 useQuery 中当 fetchStatus 字段在为 idle 时,表示当前查询函数不在运行,处于摸鱼状态^ ^
fetchStatus 一共有三个状态,fetching 状态表示当前查询函数正在运行,idle 状态表示当时查询函数不在运行。paused 状态表示查询函数尝试运行,但是无法进行请求,最可能的原因是由于当前没有联网,处于离线状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
   | import * as React from "react"; import { useQuery } from "react-query"; import "./style.css";
  const IssueLabelFilter = ({ owner, repo }) => {   const labelsQuery = useQuery(["repos", owner, repo, "labels"], () =>     fetch(`https://api.github.com/repos/${owner}/${repo}/labels`).then((res) =>       res.json()     )   );
    const labels = labelsQuery.data;
    const issuesQuery = useQuery(     ["repos", owner, repo, "issues"],     () =>       fetch(         `https://api.github.com/repos/${owner}/${repo}/issues?labels=${labels[1].name}`       ).then((res) => res.json()),     {       enabled: !!labels,      }   );
    return (     <div>       <h2>标签</h2>       {labelsQuery.isLoading ? (         <p>加载标签中...</p>       ) : (         <ul>           {labelsQuery.data.map((label) => (             <li key={label.id}>{label.name}</li>           ))}         </ul>       )}       <hr />       // 👇🏻下面的代码判断了在查询函数处于摸鱼状态时,不显示任何内容 ②{issuesQuery.isLoading &&       issuesQuery.fetchStatus === "idle" ? null : (         <div>           <h2>             Issues             {Array.isArray(issuesQuery.data) ? `(${labels[1].name})` : ""}           </h2>           // 当查询函数处于干活状态时,显示加载issues中 ③{issuesQuery.isLoading ? (             <p>加载issues中...</p>           ) : (             <ul>               {issuesQuery.data.map((issue) => (                 <li key={issue.id}>{issue.title}</li>               ))}             </ul>           )}         </div>       )}     </div>   ); };
  export default function App() {   return (     <div>       <IssueLabelFilter owner={"facebook"} repo={"react"} />     </div>   ); }
   | 
 
useMutation
使用useMutation进行增删除操作.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
   | function App() {   const mutation = useMutation((newTodo) => axios.post("/todos", newTodo));
    return (     <div>       {mutation.isLoading ? (         "Adding todo..."       ) : (         <>           {mutation.isError ? (             <div>An error occurred: {mutation.error.message}</div>           ) : null}
            {mutation.isSuccess ? <div>Todo added!</div> : null}
            <button             onClick={() => {               mutation.mutate({ id: new Date(), title: "Do Laundry" });             }}           >             Create Todo           </button>         </>       )}     </div>   ); }
  | 
 
mutate 函数是一个异步函数,这意味着你不能在事件回调中直接使用它 (React16 及之前版本)。 如果你需要在 onSubmit 中访问事件,则需要将 mutate 包装在另一个函数中。 这是由于 React 事件池限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   |  const CreateTodo = () => {   const mutation = useMutation((event) => {     event.preventDefault();     return fetch("/api", new FormData(event.target));   });
    return <form onSubmit={mutation.mutate}>...</form>; };
 
  const CreateTodo = () => {   const mutation = useMutation((formData) => {     return fetch("/api", formData);   });   const onSubmit = (event) => {     event.preventDefault();     mutation.mutate(new FormData(event.target));   };
    return <form onSubmit={onSubmit}>...</form>; };
 
  | 
 
重置修改状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
   | const CreateTodo = () => {   const [title, setTitle] = useState("");   const mutation = useMutation(createTodo);
    const onCreateTodo = (e) => {     e.preventDefault();     mutation.mutate({ title });   };
    return (     <form onSubmit={onCreateTodo}>       {mutation.error && (         <h5 onClick={() => mutation.reset()}>{mutation.error}</h5>       )}       <input         type="text"         value={title}         onChange={(e) => setTitle(e.target.value)}       />
        <button type="submit">Create Todo</button>     </form>   ); };
  | 
 
总结
通过在查询函数中,使用Promise.all来进行接口的合并请求
通过useQueries来进行动态的并行请求
isLoading为true表示第一次请求未返回结果前这段时间的状态,如果对接口进行了禁用,可以通过fetchStatus为idle来获取接口禁用请求这段时间的状态。
缓存状态
react-query 通常在挂载组件时获取数据;在获取数据后,将数据存储到缓存中,并将该数据提供给组件使用。
在获取数据时,有三种状态.loading,success,error.
也对应useQuery钩子中的isLoading、isSuccess、isError属性
当 react-query 进行后端请求查询时,会有以下三个状态:
idle:空闲,表示当前不需要从后端获取数据 
fetching: 获取数据,表示当前正在从后端获取数据 
paused:暂停,表示原本尝试从后端获取数据,但是通常由于未联网的原因导致暂停 
fetchStatus将会在 idle、fetching、paused 这三个状态间经历循环
在 react-query 中 status 为loading状态(或者isLoading为 true)指的是第一次从后端获取成功之前的状态.
而 fetchStatus 为fetching状态(或者isFetching为 true)指的是每次从后端获取数据的加载状态(包含第一次获取数据)。
整个生命周期:
假如你使用 react-query 从 Github 的接口请求了 react 的 issue 列表,你此次的请求结果将会在 status 中标记为 success 或 error,或者从 isSuccess、isError 中判断请求成功或者失败。
请求后端数据成功后,在写入缓存时,此时的缓存状态是fresh(最新)状态,但是很快(默认过期时间是 0ms)就会变为stale(老旧)状态。
如果使用 react 的 issue 列表的每个组件都被卸载后,issue 列表数据的缓存状态将会被标记为inactive(不活跃)状态。此时数据将不会被删除,直到一段时间后(默认为 5 分钟),react-query 将会从缓存中删除该条数据。
在变为inactive(不活跃)状态之前,该条数据将会在fresh(最新)与stale(老旧)之间来回切换,同时接口请求状态也会在idle与fetching 之间切换。
fresh 最新态和 stale 老旧态
react-query 是否会触发查询函数,并从后端接口获取数据,与缓存状态是:fresh(最新)状态或stale(老旧)状态有关。如果缓存状态是stale(老旧),表示该查询将会有资格重新获取,但如果缓存状态是fresh(最新)的,就不会重新获取。
如果使用 react 的 issue 列表的每个组件都被卸载后,issue 列表数据的缓存状态将会被标记为inactive(不活跃)状态。此时数据将不会被删除,直到一段时间后(默认为 5 分钟),react-query 将会从缓存中删除该条数据。
在变为inactive(不活跃)状态之前,该条数据将会在fresh(最新)与stale(老旧)之间来回切换,同时接口请求状态也会在idle与fetching之间切换。
staleTime
在默认情况下,后端返回的数据其缓存状态将会立即({staleTime: 0})从fresh(最新)状态变为stale(老旧)状态。
其实这样做不难理解,因为当你请求到数据后,后端的数据有可能就发生了变化,因此当拿到后端返回的数据瞬间,缓存状态就是stale(陈旧)的了。
你可以将配置中的staleTime,设置一个毫秒数的数字,那么缓存将会在staleTime毫秒后过期(从fresh(最新)变为stale(陈旧))
如果把staleTime设置为Infinity,表示当前查询的数据将只会获取一次,且会在整个网页的生命周期内缓存。
触发条件
在 react-query 中并不是缓存从 fresh(最新)转换为 stale(老旧)状态时,就会重新获取。
- 当组件首次加载,将会触发数据的获取。如果组件被卸载后再次被加载,此时也会触发数据的重新获取。
 
- 当用户把浏览器重新聚焦,或者切换到当前选项卡。这个触发条件是默认开启的,如果希望关闭这个触发条件,可以把
refetchOnWindowFocus选项设置为 false 来禁止。 
- 网络重新连接。可以使用
refetchOnReconnect来禁止。 
- 定时刷新。当你在配置中设置
refetchInterval为数字(代表 xxx 毫秒)时。无论此时数据是fresh(最新)还是stale(老旧)的缓存状态,react-query 都会在你设置的毫秒时间间隔内重新获取数据 
清理缓存
在缓存状态处于inactive(不活跃)状态或者使用这个查询数据的组件卸载时,超过 5 分钟(默认情况下)后,react-query 将会自动清除该缓存。
如果你希望自定义这个时间,你可以使用cacheTime配置,下面的例子中将cacheTime设置为 0,实现的效果是,当查询数据在inactive状态时,立即从缓存中删除。
1 2 3 4 5 6
   | const userQuery = useQuery(   ["user", username],   () =>     fetch(`https://api.github.com/users/${username}`).then((res) => res.json()),   { cacheTime: 0 } );
   | 
 
处理错误
错误重试
当一个查询无法重新获取时,将会基于指数退避算法的时间间隔,尝试请求 3 次。 从 1s 的延迟起步,到 30s 的最大延迟。下面是默认重试策略的相关配置:
1 2 3 4 5 6
   | const exampleQuery = useQuery("example", fetchExample, {   retry: 3,   retryDelay: (attempt) => {     return Math.min(attempt > 1 ? 2 ** attempt * 1000 : 1000, 30 * 1000);   }, });
  | 
 
PS: 如果只配置了 retry 的次数,那么 retry 的时间间隔,将会默认采用指数退避算法。
错误边界
使用react-error-boundary这个包,来减少相关错误边界的配置。
需要在配置项中,配置useErrorBoundary为true,之后错误边界就能捕获到相关的报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
   | import * as React from "react"; import "./style.css"; import { ErrorBoundary } from "react-error-boundary"; import { useQuery } from "react-query";
  const fetchWithError = async (url, options = {}) => {   const response = await fetch(url, options);
    let errorMessage = "";   if (response.status !== 200) {     errorMessage += `请求错误状态码为: ${response.status}. `;   }
    const body = await response.json();   if (body.message) {     errorMessage += body.message;   }
    console.log(errorMessage);
    if (errorMessage) {     throw new Error(errorMessage);   }
    return body; };
  const Repos = () => {   const reposQuery = useQuery(     ["users"],     () => fetchWithError("https://api.github.com/users/facebook/repos"),     {       useErrorBoundary: true,       onError: (error) => console.log(error, "onError"),     }   );
    return <div>{JSON.stringify(reposQuery.data)}</div>; };
  const Gists = () => {   const gistsQuery = useQuery(     ["gists"],     () => fetchWithError("https://api.github.com/users/facebook/gists"),     {       useErrorBoundary: true,       onError: (error) => console.log(error, "onError"),     }   );
    return <div>{JSON.stringify(gistsQuery.data)}</div>; };
  function QueryError({ error }) {   return (     <div>       <h1>出现错误</h1>       <p>{error.message}</p>     </div>   ); }
  export default function App() {   return (     <ErrorBoundary FallbackComponent={QueryError}>       <Repos />       <Gists />     </ErrorBoundary>   ); }
   | 
 
错误回调
在 react-query 中,你可以在配置中设置 onError 属性,来获取错误的回调。包含了 error 的数据。
你可以在回调中,调用消息弹窗来提示报错信息,如果你使用 ant-design 组件库,你可以这么写:
1 2 3 4 5 6 7
   | const reposQuery = useQuery(   ["users"],   () => fetchWithError("https://api.github.com/users/facebook/repos"),   {     onError: (error) => message.error({ content: error.message }),   } );
   | 
 
针对重新获取数据失败处理的方式
在第一次请求数据时,查询函数返回成功。在页面失焦后,重新聚焦,触发第二次请求时,查询函数返回失败。
如何处理?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
   | import * as React from "react"; import { useQuery } from "react-query";
  let count = 0;
  export default function App() {   const mockQuery = useQuery(     ["mock"],     async () => {       await new Promise((resolve) => setTimeout(resolve, 1000));       if (count === 0) {         count++;         return "成功";       } else {         throw new Error("失败");       }     },     { retry: false }   );
    return (     <div>          {" "}       {mockQuery.isError ? (         <div className="error-message">{mockQuery.error.message}</div>       ) : null}           {mockQuery.isLoading ? "首次加载中..." : mockQuery.data}        </div>   ); }
   | 
 
你可以根据不同的情况来指定你的显示策略:
- 当重新获取数据失败后,你可以像上面的例子一样即显示缓存数据,又显示报错
 
- 也可以判断 isError 字段为 true 时,只显示报错信息,隐藏缓存数据。
 
更多内容
建议看官方文档