用 React 构建 Web 应用时,经常需要从外部 API 获取数据并显示在界面上。除了获取数据,还要处理加载与失败状态,并通过缓存避免反复请求同样的数据。
TanStack Query
就是为解决这些问题而出现的工具之一。它以前更多以
React Query
的名字为人所知,如今已发展成支持多种框架的通用状态管理工具。本文以 React 为例,结合示例逐步介绍从基础到进阶的用法。
TanStack Query 帮助客户端应用高效处理服务器状态(server state)。简单来说,它负责管理从服务器获取的数据,简化加载、错误处理、缓存和重新请求等操作。
例如,它适用于以下情况。
- 从 API 获取文章列表并显示在页面上。
- 缓存相同请求的结果,避免重复发出请求。
- 获取数据时显示加载动画。
- 请求失败时显示错误消息。
先在项目中安装 TanStack Query。在终端输入以下命令。
npm install @tanstack/react-query
要在 React 应用中使用查询功能,还需要在应用最外层设置 QueryClientProvider。
// main.tsx 또는 App.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
root.render(
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
);
先看最基础的用法。这里以 JSONPlaceholder 的文章 API(/posts)为例。
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
const fetchPosts = async () => { // 정보를 fetch하는 함수
const res = await axios.get('https://jsonplaceholder.typicode.com/posts');
return res.data;
};
function PostList() {
const { data, isLoading, isError, error } = useQuery({ // 쿼리 결과를 가져오는 컴포넌트
queryKey: ['posts'], // 쿼리의 고유 식별자
queryFn: fetchPosts, // 정보를 fetch하는 함수
});
if (isLoading) return <p>불러오는 중입니다...</p>;
if (isError) return <p>오류가 발생했습니다: {(error as Error).message}</p>;
return (
<ul>
{data.map((post: any) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
首先定义一个获取数据的函数。它异步获取数据并返回结果。
然后把这个函数传给
useQuery 的
queryFn 选项。
useQuery 会调用该函数,并把结果存入缓存。
queryKey 在 TanStack Query 中很重要。它唯一标识请求,区分缓存,也是重新请求的依据。
简单查询可以使用字符串形式。
useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
});
如果包含 API 参数,建议组织为数组。
useQuery({
queryKey: ['user', userId],
queryFn: () => fetchUser(userId),
});
这样,不同的 userId 会被识别为不同的查询,各自使用独立缓存。
结构更复杂时,可以按下面的方式构建嵌套的 queryKey。
useQuery({
queryKey: ['users', userId, 'posts'],
queryFn: () => fetchUserPosts(userId),
});
这样就能清楚地标识特定用户的文章查询,让缓存管理更灵活。
将 React 的 Suspense 与 TanStack Query 结合使用,可以用更简洁的声明式方式处理加载状态。先用 React 的 Suspense 组件包裹发起查询的组件,再为 useQuery 设置 suspense: true。
// App.tsx
<Suspense fallback={<p>로딩 중입니다...</p>}>
<PostList />
</Suspense>
// PostList.tsx
const { data } = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
suspense: true,
});
这种方式让代码更简洁,也便于统一管理加载状态。建议搭配 ErrorBoundary 处理组件错误。
使用 React Query Devtools 🔗
开发时,还可以用 Devtools 实时查看查询状态。
npm install @tanstack/react-query-devtools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
<QueryClientProvider client={queryClient}>
<App />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
这个工具可以直观地显示当前缓存的数据、查询状态和请求时间,方便调试。
使用 Next.js 等框架进行 SSR 时,可以先在服务器执行查询,把数据包含在 HTML 中,加快首次加载。
为此可以使用
dehydrate 与
Hydrate 组件。
// 서버에서
const queryClient = new QueryClient();
await queryClient.prefetchQuery({ queryKey: ['posts'], queryFn: fetchPosts });
const dehydratedState = dehydrate(queryClient);
// 클라이언트에서
<Hydrate state={dehydratedState}>
<App />
</Hydrate>
服务器和客户端共享同一份缓存状态,因此可以直接显示数据,避免画面闪烁。
查询或 mutation 失败时,可以通知用户或重试。可用选项包括:
retry:设置自动重试次数。
onError:失败时执行回调。
useErrorBoundary:与 Suspense 搭配时,将错误传递给错误边界。
useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
retry: 2,
onError: (error) => {
toast.error('데이터를 불러오는 데 실패했습니다');
},
});
useMutation 也能用类似的方式处理错误。还可以将错误日志输出到控制台,或发送给 Sentry 等外部服务。
TanStack Query 是 React 中处理异步数据的灵活工具。除了基本的
useQuery,它还提供
useMutation、缓存、失效处理、按条件执行、无限滚动、Suspense 集成和 SSR 等功能,帮助简化复杂逻辑。
可以参考本文的示例与策略,在实际项目中应用 TanStack Query。