React Query (TanStack Query) 数据请求最佳实践
Sarah Wong | 2026-08-27T20:54:37 | JavaScript, Frontend
深入讲解 React Query 的缓存策略、乐观更新、无限滚动和离线支持等高级用法。
# React Query 数据请求最佳实践 ## 为什么用 React Query? 传统 useEffect + useState 管理请求状态痛点太多:加载态、错误态、缓存、去重、轮询都要手动实现。React Query 一站式解决。 ## 基础查询 ```tsx import { useQuery } from '@tanstack/react-query'; function UserProfile({ userId }) { const { data, isLoading, error } = useQuery({ queryKey: ['user', userId], queryFn: () => fetch('/api/users/' + userId).then(r => r.json()), staleTime: 5 * 60 * 1000, // 5 分钟内数据视为新鲜 gcTime: 30 * 60 * 1000, // 缓存保留 30 分钟 }); if (isLoading) return ; if (error) return ; return ( {data.name} {data.email} ); } ``` ## 变更操作(Mutation) ```tsx import { useMutation, useQueryClient } from '@tanstack/react-query'; function CreatePostForm() { const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (newPost) => fetch('/api/posts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newPost), }).then(r => r.json()), onSuccess: () => { // 创建成功后刷新文章列表 queryClient.invalidateQueries({ queryKey: ['posts'] }); }, }); function handleSubmit(e) { e.preventDefault(); mutation.mutate({ title: e.target.title.value, content: e.target.content.value, }); } return ( {mutation.isPending ? '发布中...' : '发布'} ); } ``` ## 乐观更新 ```tsx const likeMutation = useMutation({ mutationFn: (postId) => fetch('/api/posts/' + postId + '/like', { method: 'POST' }), onMutate: async (postId) => { await queryClient.cancelQueries({ queryKey: ['post', postId] }); const previous = queryClient.getQueryData(['post', postId]); queryClient.setQueryData(['post', postId], (old) => ({ ...old, likeCount: old.likeCount + 1, liked: true, })); return { previous }; }, onError: (err, postId, context) => { queryClient.setQueryData(['post', postId], context.previous); }, }); ``` ## 无限滚动 ```tsx import { useInfiniteQuery } from '@tanstack/react-query'; function PostList() { const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({ queryKey: ['posts'], queryFn: ({ pageParam = 1 }) => fetch('/api/posts?page=' + pageParam).then(r => r.json()), getNextPageParam: (lastPage) => lastPage.hasMore ? lastPage.nextPage : undefined, }); return ( {data.pages.map((page) => page.items.map((post) => ) )} {hasNextPage && ( fetchNextPage()} disabled={isFetchingNextPage}> {isFetchingNextPage ? '加载中...' : '加载更多'} )} ); } ``` ## 缓存策略总结 | 参数 | 含义 | 推荐值 | |------|------|--------| | staleTime | 数据新鲜期 | 按业务,1-5 分钟 | | gcTime | 缓存存活期 | 30 分钟 | | refetchOnWindowFocus | 窗口聚焦时刷新 | true | | retry | 失败重试次数 | 3 |