ReactQuery服务器状态管理Skill react-query

这是一个关于 TanStack Query(React Query)的专业技能,专注于在 React 应用中高效管理服务器状态。它提供了数据获取、缓存、变更处理、乐观更新、无限查询和预取等核心功能的实现模式与最佳实践。适用于需要优化数据流、提升应用性能和用户体验的前端开发场景。关键词:React Query,服务器状态管理,数据缓存,乐观更新,无限查询,前端性能优化,React 数据获取。

前端开发 0 次安装 2 次浏览 更新于 2/26/2026

name: react-query description: TanStack Query(React Query)模式,用于服务器状态管理、缓存、变更、乐观更新和无限查询。 allowed-tools: Read, Write, Edit, Bash, Glob, Grep

React Query 技能

为在 React 应用程序中实现 TanStack Query(React Query)进行服务器状态管理提供专家级协助。

能力

  • 使用最优默认值配置 QueryClient
  • 使用缓存策略实现查询
  • 处理带有乐观更新的变更
  • 为分页设置无限查询
  • 管理查询失效和预取
  • 与身份验证和错误处理集成

使用场景

在以下情况时调用此技能:

  • 获取和缓存服务器数据
  • 处理带有回滚的变更
  • 实现无限滚动或分页
  • 为导航预取数据
  • 同步服务器状态

输入参数

参数 类型 是否必需 描述
endpoint string 要查询的 API 端点
queryKey array 唯一的查询键
staleTime number 数据变为陈旧的时间(毫秒)
cacheTime number 在缓存中保留的时间(毫秒)
optimisticUpdate boolean 启用乐观更新

配置示例

{
  "endpoint": "/api/users",
  "queryKey": ["users"],
  "staleTime": 300000,
  "cacheTime": 600000,
  "optimisticUpdate": true
}

生成的模式

Query Client 设置

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5, // 5 分钟
      gcTime: 1000 * 60 * 30, // 30 分钟(原 cacheTime)
      retry: 3,
      retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
      refetchOnWindowFocus: false,
    },
    mutations: {
      retry: 1,
    },
  },
});

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <QueryClientProvider client={queryClient}>
      {children}
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

基础查询钩子

import { useQuery } from '@tanstack/react-query';

interface User {
  id: string;
  name: string;
  email: string;
}

async function fetchUsers(): Promise<User[]> {
  const response = await fetch('/api/users');
  if (!response.ok) {
    throw new Error('获取用户失败');
  }
  return response.json();
}

export function useUsers() {
  return useQuery({
    queryKey: ['users'],
    queryFn: fetchUsers,
  });
}

export function useUser(userId: string) {
  return useQuery({
    queryKey: ['users', userId],
    queryFn: () => fetchUser(userId),
    enabled: !!userId,
  });
}

带有乐观更新的变更

import { useMutation, useQueryClient } from '@tanstack/react-query';

interface UpdateUserDto {
  id: string;
  name?: string;
  email?: string;
}

export function useUpdateUser() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (data: UpdateUserDto) => {
      const response = await fetch(`/api/users/${data.id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });
      if (!response.ok) throw new Error('更新用户失败');
      return response.json();
    },

    onMutate: async (newData) => {
      // 取消正在进行的重新获取
      await queryClient.cancelQueries({ queryKey: ['users', newData.id] });

      // 快照先前值
      const previousUser = queryClient.getQueryData(['users', newData.id]);

      // 乐观更新
      queryClient.setQueryData(['users', newData.id], (old: User) => ({
        ...old,
        ...newData,
      }));

      return { previousUser };
    },

    onError: (err, newData, context) => {
      // 出错时回滚
      if (context?.previousUser) {
        queryClient.setQueryData(['users', newData.id], context.previousUser);
      }
    },

    onSettled: (data, error, variables) => {
      // 重新获取以确保一致性
      queryClient.invalidateQueries({ queryKey: ['users', variables.id] });
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
}

无限查询

import { useInfiniteQuery } from '@tanstack/react-query';

interface Page {
  data: User[];
  nextCursor?: string;
}

async function fetchUsersPage({ pageParam }: { pageParam?: string }): Promise<Page> {
  const url = pageParam
    ? `/api/users?cursor=${pageParam}`
    : '/api/users';
  const response = await fetch(url);
  return response.json();
}

export function useInfiniteUsers() {
  return useInfiniteQuery({
    queryKey: ['users', 'infinite'],
    queryFn: fetchUsersPage,
    initialPageParam: undefined,
    getNextPageParam: (lastPage) => lastPage.nextCursor,
    getPreviousPageParam: (firstPage) => firstPage.previousCursor,
  });
}

// 在组件中使用
function UserList() {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteUsers();

  return (
    <div>
      {data?.pages.map((page, i) => (
        <React.Fragment key={i}>
          {page.data.map((user) => (
            <UserCard key={user.id} user={user} />
          ))}
        </React.Fragment>
      ))}
      <button
        onClick={() => fetchNextPage()}
        disabled={!hasNextPage || isFetchingNextPage}
      >
        {isFetchingNextPage ? '加载中...' : hasNextPage ? '加载更多' : '没有更多了'}
      </button>
    </div>
  );
}

预取

import { useQueryClient } from '@tanstack/react-query';

function UserLink({ userId }: { userId: string }) {
  const queryClient = useQueryClient();

  const prefetchUser = () => {
    queryClient.prefetchQuery({
      queryKey: ['users', userId],
      queryFn: () => fetchUser(userId),
      staleTime: 1000 * 60 * 5,
    });
  };

  return (
    <Link
      to={`/users/${userId}`}
      onMouseEnter={prefetchUser}
      onFocus={prefetchUser}
    >
      查看用户
    </Link>
  );
}

查询键工厂

export const userKeys = {
  all: ['users'] as const,
  lists: () => [...userKeys.all, 'list'] as const,
  list: (filters: Filters) => [...userKeys.lists(), filters] as const,
  details: () => [...userKeys.all, 'detail'] as const,
  detail: (id: string) => [...userKeys.details(), id] as const,
};

// 使用
useQuery({ queryKey: userKeys.detail(userId), ... });
queryClient.invalidateQueries({ queryKey: userKeys.lists() });

最佳实践

  • 使用查询键工厂以确保一致性
  • 为数据设置适当的陈旧时间
  • 实现乐观更新以提升用户体验
  • 策略性地使用 placeholderData 或 initialData
  • 将服务器状态与客户端状态分离

目标流程

  • react-application-development
  • nextjs-full-stack
  • data-fetching-setup
  • performance-optimization