react-query是什么
react-query适用于React Hooks的,方便我们管理服务端请求的一个库。使用这个库你可以很方便的获取、同步、更新和缓存你的远程数据。
主要功能:
- 管理请求
- 可以实现请求、轮询、失败重试、无限加载等功能
- 可以在网络重连、窗口获得焦点等时机等向服务器发送请求同步状态
- 状态管理
- 可以把服务端的状态缓存在客户端的内存中, 从而让任意组件获取这些状态。
安装
$ npm i react-query
# or
$ yarn add react-query
基本使用
main.jsx (项目入口文件)
import React from 'react'
import ReactDOM from 'react-dom/client'
import { QueryClientProvider, QueryClient } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'
import App from './App'
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById('root')).render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
)
app.jsx
import { useQuery } from 'react-query';
import request from './request';
function App() {
// 查询
const userQuery = useQuery('users', () => request.get('/users'));
console.log(userQuery);
return (
(<ul>
{
userQuery?.data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>)
)
}
export default App
开发工具
- devtools 是React Query带有专用的可视化开发工具,可以清晰的观察每个请求的
缓存key和其相关状态。 - 配置后, 会在页面中出现一个小图标,另外工具相关代码不会打包到生产环境中。
main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { QueryClientProvider, QueryClient } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'
import App from './App'
const queryClient = new QueryClient();
ReactDOM.createRoot(document.getElementById('root')).render(
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOp={true} position='bottom-right'/>
</QueryClientProvider>
)
常用的两个属性:
- initialIsOp: 是否默认打开
- position: 图标显示位置
数据状态
- fetching: 请求中。
- fresh: 新鲜的。默认情况下,数据一旦被缓存就变为过时的了, 可以通过延长
staleTime时间, 来保持数据“新鲜”。 - stale: 过时的。表示数据已经过时,需要重新通过网络请求获取新数据。
- inactive: 未激活的。当查询结果不再被使用时,该查询结果将被标记为"非活动",并保留在缓存中,以防以后再次使用,在超过缓存时间限制后被垃圾收集。
数据为stale状态时,什么情况可能下会再次请求
- 主动调用
query.refresh方法, 进行请求。 - 窗口重新聚焦
- 网络重新连接
- 有轮训配置
- 挂载新的查询实例,如
query依赖的参数发生改变
查询
- 在
react-query中, 使用useQuery或者useInfiniteQuery获取数据。常用的写法如下:
useQuery(queryKey, fn, options)
- queryKey:当前查询的一个唯一的键值
- fn: 一个返回 Promise 的函数。
- options: 可选。配置项
配置(options)
| 字段 | 含义 | 说明 |
|---|---|---|
| staleTime | 数据过时时间,即在时间范围内,再次发起请求时,直接使用缓存数据,默认请求完成后数据直接进入过时状态 | 0 默认立刻过期 |
| cacheTime | 数据缓存时间,即到时间后,数据没有被使用,则会被GC掉 | 1000 * 60 * 5 默认5分钟 |
| retry | 失败重试次数 | 3 默认重试三次 |
| retryDelay | 失败重试间隔时间,可以接收具体的时间,也可以接收函数,比如返回递增时间的函数 | |
| refetchOnWindowFocus | 窗口重新获得焦点时重新获取数据 | true |
| refetchOnReconnect | 网络重新链接时重新获取数据 | |
| refetchInterval | 轮询时间 | |
| enabled | 是否可用,为false不会发起请求 | |
| initialData | 初始化数据,一般用于已经有完整的数据但是不确定数据是否已经变更的场景,initialData 受staleTime影响,如果初始化数据没过时,就会一直使用初始化数据, 否则重新发起请求 | |
| initialStale | 初始化数据标记为是否过期 | true (默认过期) |
| placeholderData | 占位数据, 类似于initialData选项,但是数据不会持久保存到缓存中, 一般是部分或不完整的数据 | |
| keepPreviousData | 保留以前数据, 主要用于请求loading时,可以使用之前的数据,等新数据来时无缝切换 | |
| onSuccess | 成功的回掉 | (data) => {} |
| onError | 失败的回掉 | (error) => {} |
| onSettled | 无论成功或者失败都会执行的回掉 | (data,error) => {} |
staleTime 和 cacheTime
2.staletime.jsx
import { useQuery } from 'react-query';
import request from './request';
function App() {
const { data, isLoading, isError } = useQuery('users', () => request.get('/users'), {
staleTime: 5000
})
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>)
)
}
export default App;
3.cachetime.jsx
import { useState } from "react";
import { useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function Users() {
const { data, isLoading, isError, isFetching } = useQuery(
"users",
() => request.get("/users"),
{
refetchOnWindowFocus: true,
staleTime: Infinity, // 永不过期
cacheTime: 5000,
}
);
if (isLoading) return <div>加载中.......</div>;
if (isError) return <div>加载失败</div>;
return (
<>
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
);
}
function App() {
const [show, setShow] = useState(true);
return (
<>
<button onClick={() => setShow(!show)}>{show ? "隐藏" : "显示"}</button>
{show && <Users />}
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}
export default App;
查询键(queryKey)
react-query是基于查询键值管理查询缓存。- 查询键值可以是一个
字符串,也可以是由许多字符串和嵌套对象组成的数组,但是需要保证键值是可序列化的 - 查询键值序列化后, 相同的会被去重。请求依赖的参数需要放到查询键中,否则相关参数变更时,可能不会重新请求。
// 字符串
useQuery('todos', ...) // queryKey === ['todos']
// 数组项的顺序影响序列化后的值
useQuery(['todos', status, page], ...)
// 对象的key的顺序不影响序列化后的值
useQuery(['todos', { page, status }], ...)
查询键去重
4.querykey.jsx
import { useState } from 'react';
import { useQuery } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function Users({ queryKey }) {
const { data, isLoading, isError, isFetching } = useQuery(queryKey, () => request.get('/users'))
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(
<>
<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
)
)
}
function App() {
return (
<>
<Users queryKey="users" />
<Users queryKey="users" />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
查询函数
- 查询函数实际上可以是任何一个返回 Promise 的函数。返回的 Promise 应该返回数据或引发错误。
// 查询函数可以解构查询键中的参数
function Todos({ status, page }) {
const result = useQuery(["todos", { status, page }], fetchTodoList);
}
// 在查询函数中访问键值,状态和页面变量!
function fetchTodoList({ queryKey }) {
const [_key, { status, page }] = queryKey;
return new Promise();
}
import { useQuery } from "react-query";
useQuery({
queryKey: ["todo", 7],
queryFn: fetchTodo,
...config,
});
query实例
在query实例中, 包含有很多和状态有关的字段, 我们可以用这些不同的请求状态, 呈现给用户不同的展示, 对一些常用字段做下说明
| 字段 | 含义 | 取值 | 说明 |
|---|---|---|---|
| status | 状态 | loading、error、success | 你可以选择使用status === 'loading'这种形式做请求状态的判断, 也可以直接使用isLoading字段 |
| isLoading | 是否首次加载中 | true、false | 注意首次加载请求中,不要用这个字段表示请求的loading状态,一般使用isFetching |
| isFetching | 是否正在请求 | true、false | |
| isError | 是否获取失败 | true、false | |
| isSuccess | 是否获取成功 | true、false | |
| isIdle | 是否空闲,即当前query是否发起过请求 | true、false | |
| isPreviousData | 是否是老数据 | false |
方法
| 字段 | 含义 |
|---|---|
| refetch | 用于手动重新请求 |
import { useQuery } from 'react-query';
import request from './request';
function App() {
const { data, isLoading, isError } = useQuery('users', () => request.get('/users'))
if (isLoading) return <div>加载中.......</div>
if (isError) return <div>加载失败</div>
return (
(<ul>
{
data?.map((user) => <li key={user.id}>{user.name}</li>)
}
</ul>)
)
}
export default App;
全局状态处理方式
自定义hooks
- 针对多处调用的公用请求可以抽离成
hooks,起到类似全局状态管理器的作用。
import { useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function useUsers() {
return useQuery("users", () => request.get("/users"));
}
function Stats() {
const { data } = useUsers();
return data && <h1>共计{data.length}用户</h1>;
}
function Users() {
const { data, isLoading, isError, isFetching } = useUsers();
if (isLoading) return <div>加载中.......</div>;
if (isError) return <div>加载失败</div>;
return (
<>
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
);
}
function App() {
return (
<>
<Users />
<Stats />
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}
export default App;
QueryObserver
- 针对不好抽离的逻辑可以采用 QueryObserver 的方式, 进行订阅处理。
- QueryObserver 可实现在任意组件中订阅状态
import { useEffect, useState } from "react";
import { useQuery, QueryObserver, useQueryClient } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function Stats() {
const [data, setData] = useState();
const queryClient = useQueryClient();
useEffect(() => {
const observer = new QueryObserver(queryClient, { queryKey: "users" });
const unsubscribe = observer.subscribe((result) => setData(result.data));
return unsubscribe;
}, []);
return data && <h1>共计{data.length}用户</h1>;
}
function Users() {
const { data, isLoading, isError, isFetching } = useQuery("users", () =>
request.get("/users")
);
if (isLoading) return <div>加载中.......</div>;
if (isError) return <div>加载失败</div>;
return (
<>
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
{isFetching && <div>更在更新数据...</div>}
</>
);
}
function App() {
return (
<>
<Users />
<Stats />
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}
export default App;
取消请求
9.cancelRequest.jsx
import React from "react";
import { useQuery } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request, { CancelToken } from "./request";
function User({ userId }) {
const { data, isLoading, isError, error, isFetching, isIdle } = useQuery(
["user", userId],
({ signal }) => {
// 旧版取消请求
// const source = CancelToken.source();
// const promise = request
// .get("/user", {
// params: { userId },
// cancelToken: source.token,
// })
// .catch((error) => {
// if (request.isCancel(error)) {
// console.log(error.message);
// }
// });
// // react-query 需要取消请求的时候, 会调用返回的promise的cancel方法
// promise.cancel = () => source.cancel("请求被React Query取消");
// return promise;
// 新版取消请求
return request.get("/user", {
params: { userId },
signal,
});
},
{
enabled: !!userId,
retry: 3,
retryDelay: 1000,
}
);
console.log(data);
if (isIdle) return null;
if (isLoading) return <div>加载中.......</div>;
if (isError) return <div>{error.message}</div>;
return (
<>
{data.id ? (
<p>
{data.id}:{data.name}
</p>
) : (
<p>{userId}对应的用户不存在</p>
)}
{isFetching && <div>更在更新数据...</div>}
</>
);
}
function App() {
const [userId, setUserId] = React.useState("");
return (
<>
<input
value={userId}
onChange={(event) => setUserId(event.target.value)}
/>
<User userId={userId} />
<ReactQueryDevtools initialIsOpen={true} />
</>
);
}
export default App;
request.js
import axios, { CancelToken } from 'axios';
axios.interceptors.response.use(response => response.data);
axios.defaults.baseURL = 'http://localhost:8080';
export default axios;
export { CancelToken }
并发查询
- 当存在几个请求需要并发查询时, 可以使用
useQueries,它接收一组查询配置对象。 - 其实
useQuery也是异步的,不会阻塞下放代码执行,但是在React.suspense模式下就又会阻塞了, 存在一些问题
import React from 'react';
import { useQuery, useQueries } from 'react-query';
import { ReactQueryDevtools } from 'react-query/devtools'
import request from './request';
function User() {
const [usersQuery, infosQuery] = useQueries([
{ queryKey: ['users'], queryFn: () => request.get('/users') },
{ queryKey: ['infos'], queryFn: () => request.get(`/infos`) },
]);
return (
(
<>
{usersQuery.data && <p>用户数:{usersQuery.data.length}</p>}
{infosQuery.data && <p>帖子数:{infosQuery.data.length}</p>}
</>
)
)
}
function App() {
return (
<>
<User />
<ReactQueryDevtools initialIsOpen={true} />
</>
)
}
export default App;
读取查询缓存
- QueryCache是 React Query 的存储机制。它存储它包含的所有数据、元信息和查询状态
- 通常不会直接与 QueryCache 交互,而是使用QueryClient,查询缓存数据
import React from "react";
import { useQuery, useQueryClient } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function Users({ setUserId }) {
const usersResult = useQuery("users", () => request.get("/users"), {
staleTime: 5000,
});
if (usersResult.isLoading) {
return "用户列表加载中......";
}
return (
<>
<h3>用户列表</h3>
<ul>
{usersResult.data?.map((user) => (
<li key={user.id} onClick={() => setUserId(user.id)}>
{user.name}
</li>
))}
</ul>
</>
);
}
function User({ userId, setUserId }) {
// 获取查询客户端
const queryClient = useQueryClient();
const userResult = useQuery(
["user", userId],
() =>
request.get("/user", {
params: { userId },
}),
{
staleTime: 5000,
initialData: () => queryClient.getQueryData("users")?.find((user) => user.id === userId),
// 初始化数据时在缓存中查询到自己想要的数据, 否则发起请求
initialStable: true,
}
);
if (userResult.isLoading) {
return "单个用户加载中......";
}
return (
<div>
<button onClick={() => setUserId(-1)}>返回</button>
{userResult.data && (
<p>
ID:{userResult.data.id},NAME:{userResult.data.name}
</p>
)}
</div>
);
}
function App() {
const [userId, setUserId] = React.useState(-1);
return (
<>
{userId > -1 ? (
<User userId={userId} setUserId={setUserId} />
) : (
<Users setUserId={setUserId} />
)}
<ReactQueryDevtools initialIsOpen={false} />
</>
);
}
export default App;
预缓存
- 对于某些数据, 我们在某些接口中获取到了, 可能在其他位置还需要通过接口获取,我们可以将这部分数据提前缓存到
queryClient中,提升用户体验,减少loading或者白屏
import React from "react";
import { useQuery, useQueryClient } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function Users({ setUserId }) {
const queryClient = useQueryClient();
const usersResult = useQuery(
"users",
async () => {
const users = await request.get("/users");
users.forEach((user) => {
// 将每个用户详情信息,缓存到queryClient
queryClient.setQueryData(["user", user.id], user);
});
return users;
},
{
staleTime: 5000,
}
);
if (usersResult.isLoading) {
return "用户列表加载中......";
}
return (
<>
<h3>用户列表</h3>
<ul>
{usersResult.data?.map((user) => (
<li key={user.id} onClick={() => setUserId(user.id)}>
{user.name}
</li>
))}
</ul>
</>
);
}
function User({ userId, setUserId }) {
const userResult = useQuery(["user", userId], () =>
request.get("/user", {
params: { userId },
})
);
if (userResult.isLoading) {
return "单个用户加载中......";
}
return (
<div>
<button onClick={() => setUserId(-1)}>返回</button>
{userResult.data && (
<p>
ID:{userResult.data.id},NAME:{userResult.data.name}
</p>
)}
</div>
);
}
function App() {
const [userId, setUserId] = React.useState(-1);
return (
<>
{userId > -1 ? (
<User userId={userId} setUserId={setUserId} />
) : (
<Users setUserId={setUserId} />
)}
<ReactQueryDevtools initialIsOpen={false} />
</>
);
}
export default App;
预查询
prefetchQuery是一种异步方法, 用法和useQuery一样。- 通过预测用户的行为, 提前进行请求,从而达到提前呈现数据的目的。比如:鼠标划过详情按钮时就去请求详情信息、前后分页场景
import React from "react";
import { useQuery, useQueryClient } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function fetchUsers() {
return request.get("/users");
}
function Users({ setUserId }) {
const usersResult = useQuery("users", fetchUsers);
if (usersResult.isLoading) {
return "用户列表加载中......";
}
return (
<>
<h3>用户列表</h3>
<ul>
{usersResult.data?.map((user) => (
<li key={user.id} onClick={() => setUserId(user.id)}>
{user.name}
</li>
))}
</ul>
</>
);
}
function App() {
const queryClient = useQueryClient();
const [show, setShow] = React.useState(false);
return (
<>
<button
onClick={() => setShow(!show)}
onMouseOver={() =>
// 点那个用户有鼠标滑入按钮时,我们预测用户可能想查看用户列表, 进行预查询, 真正请求的时候, 就直接使用缓存了
queryClient.prefetchQuery("users", fetchUsers, { staleTime: 5000 })
}
>
show
</button>
{show && <Users />}
<ReactQueryDevtools initialIsOpen={false} />
</>
);
}
export default App;
分页查询
import React from "react";
import { useQuery, useQueryClient } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function fetchUsers({queryKey: [_, { pageNumber }]}) {
return request.get("/usersByPage", {
params: {
pageNumber,
},
});
}
function Users() {
const queryClient = useQueryClient();
const [pageNumber, setPageNumber] = React.useState(1);
const usersResult = useQuery(["users", { pageNumber }], fetchUsers, {
onSuccess() {
queryClient.prefetchQuery(
["users", { pageNumber: pageNumber + 1 }],
fetchUsers
);
},
keepPreviousData: true
// 1. 请求新数据时,即使查询键值已更改,上次成功获取的数据仍可用
// 2. 当新数据到达时,先前的数据将被无缝交换以显示新数据
// 3. 可以使用isPreviousData来了解当前为您提供的是什么数据
});
return (
<>
<h3>用户列表</h3>
<ul>
{usersResult.data?.list.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
{
<button
disabled={pageNumber <= 1}
onClick={() => setPageNumber((pageNumber) => pageNumber - 1)}
>
上一页
</button>
}
<span>{pageNumber}</span>
{
<button
disabled={usersResult.data?.pageNumber >= usersResult.data?.totalPage}
onClick={() => setPageNumber((pageNumber) => pageNumber + 1)}
>
下一页
</button>
}
</>
);
}
function App() {
return (
<>
<Users />
<ReactQueryDevtools initialIsOpen={false} />
</>
);
}
export default App;
无限分页
import React from "react";
import { useQuery, useInfiniteQuery } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function fetchUsers({ pageParam = 1 }) {
// 使用 useInfiniteQuery 时, 在 pageParam 获取页码
return request.get("/usersByPage", {
params: {
pageNumber: pageParam,
},
});
}
function Users() {
const { data, hasNextPage, fetchNextPage } = useInfiniteQuery(
["usersByPage"],
fetchUsers,
{
getNextPageParam: (lastPageData) => {
// 返回下一页的页码
return lastPageData.pageNumber < lastPageData.totalPage
? lastPageData.pageNumber + 1
: false;
},
}
);
// hasNextPage 表示是否还有下一页
// fetchNextPage 用来请求下一页
return (
<>
<h3>用户列表</h3>
<ul>
{data?.pages?.map((page, index) => {
return (
<React.Fragment key={index}>
{page.list?.map((user) => (
<li key={user.id}>
{user.id}:{user.name}
</li>
))}
</React.Fragment>
);
})}
</ul>
<button disabled={!hasNextPage} onClick={() => fetchNextPage()}>
加载更多
</button>
</>
);
}
function App() {
return (
<>
<Users />
<ReactQueryDevtools initialIsOpen={false} />
</>
);
}
export default App;
变更
react-query中使用useMutation进行对数据的创建/更新/删除操作useMutation返回值字段含义同useQuery
基本使用
import React from "react";
import { useQuery, useQueryClient, useMutation } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function fetchUsers() {
return request.get("/users");
}
function Users({ setUserId }) {
const usersResult = useQuery("users", fetchUsers);
if (usersResult.isLoading) {
return "用户列表加载中......";
}
return (
<>
<h3>用户列表</h3>
<ul>
{usersResult.data?.map((user) => (
<li key={user.id} onClick={() => setUserId(user.id)}>
{user.name}
</li>
))}
</ul>
</>
);
}
function App() {
const nameRef = React.useRef();
const queryClient = useQueryClient();
const { mutate, mutateAsync, isLoading, isError, isSuccess, error, reset } = useMutation(
(values) => request.post("/users", values),
{
onSuccess() {
// 表示重置请求状态
reset()
// 表示让users缓存失效, 并且立刻发起请求, 刷新列表更加优雅
// 此时表示 queryKey 以 users 开头的都会失效, 但是实际场景中也可以将粒度更加细化
queryClient.invalidateQueries('users');
},
onError(error) {
//alert(error.response.data.message);
},
onSettled(data, error) {
// queryClient.invalidateQueries("users");
},
}
);
const handleSubmit = (event) => {
event.preventDefault();
const name = nameRef.current.value;
const user = { name };
// mutate接收的参数会传递到useMutation的第一个会掉函数中
mutate(user);
// mutateAsync(user) 返回的是一个Promise
};
return (
<>
<Users />
<form onSubmit={handleSubmit}>
<input ref={nameRef} />
<input
type="submit"
value={
isLoading
? "保存中..."
: isError
? "保存失败"
: isSuccess
? "保存成功"
: "保存"
}
/>
</form>
{isError && (
<pre style={{ color: "red" }}>{error.response.data.message}</pre>
)}
<ReactQueryDevtools initialIsOpen={false} />
</>
);
}
export default App;
乐观更新和失败回滚
- 乐观更新指的是对于成功率极大的操作, 我们乐观的认为一定会成功, 我们将某部分数据提前呈现给用户, 而不是在刷库之后呈现给用户, 提高用户体验。
- 当乐观更新中真的出现了失败的情况, 我们需要失败回滚
onMutate函数将在突变函数被触发之前触发,并传递突变函数将接收的相同变量- 如果发生变更失败,函数返回的值将传递给
onError和onSettled函数,并且可用于回滚乐观更新
import React from "react";
import { useQuery, useQueryClient, useMutation } from "react-query";
import { ReactQueryDevtools } from "react-query/devtools";
import request from "./request";
function fetchUsers() {
return request.get("/users");
}
function Users({ setUserId }) {
const usersResult = useQuery("users", fetchUsers);
if (usersResult.isLoading) {
return "用户列表加载中......";
}
return (
<>
<h3>用户列表</h3>
<ul>
{usersResult.data?.map((user) => (
<li key={user.id} onClick={() => setUserId(user.id)}>
{user.name}
</li>
))}
</ul>
</>
);
}
function App() {
const nameRef = React.useRef();
const queryClient = useQueryClient();
const {
mutate: saveUser,
isLoading,
isError,
isSuccess,
error,
reset,
} = useMutation((values) => request.post("/users", values), {
// 回滚方式一, 直接在onMutate将老状态返回, 失败后直接状态回退
// onMutate(values) {
// const oldUsers = queryClient.getQueryData("users");
// queryClient.setQueryData("users", (oldUsers) => [
// ...oldUsers,
// { ...values, id: String(Date.now()) },
// ]);
// return oldUsers;
// },
// onError(error, values, rollbackValues) {
// queryClient.setQueriesData('users', rollbackValues);
// },
// 回滚方式二, 直接在onMutate返回一个函数, 失败后直接调用
onMutate(values) {
const oldUsers = queryClient.getQueryData("users");
queryClient.setQueryData("users", (oldUsers) => [
...oldUsers,
{ ...values, id: String(Date.now()) },
]);
return () => queryClient.setQueryData('users', oldUsers);
},
onError(error, values, rollback) {
rollback()
},
onSuccess() {
reset();
queryClient.invalidateQueries("users");
},
onSettled(data, error) {
// queryClient.invalidateQueries('users');
},
});
const handleSubmit = (event) => {
event.preventDefault();
const name = nameRef.current.value;
const user = { name };
saveUser(user);
};
return (
<>
<Users />
<form onSubmit={handleSubmit}>
<input ref={nameRef} />
<input
type="submit"
value={
isLoading
? "保存中..."
: isError
? "保存失败"
: isSuccess
? "保存成功"
: "保存"
}
/>
</form>
{isError && (
<pre style={{ color: "red" }}>{error.response.data.message}</pre>
)}
<ReactQueryDevtools initialIsOpen={false} />
</>
);
}
export default App;
查询客户端
- 我们可以通过操作
查询客户端来管理我们的缓存。
查询过滤器
import { useQueryClient } from "react-query";
const queryClient = useQueryClient();
// 取消所有查询
await queryClient.cancelQueries();
// 删除所有以`posts`开头的键值的非活动查询
queryClient.removeQueries("posts", { inactive: true });
// 重新获取所有活动查询
await queryClient.refetchQueries({ active: true });
// 重新获取键中以`posts`开头的所有活动查询
await queryClient.refetchQueries("posts", { active: true });
// 获取所有正在获取的修改的数量
await queryClient.isMutating();
// 通过 mutationKey 过滤
await queryClient.isMutating({ mutationKey: "post" });
// 使用谓词函数过滤
await queryClient.isMutating({
predicate: (mutation) => mutation.options.variables?.id === 1,
});