You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TanStack Query实现是否合理?求优化方案建议

TanStack Query 实现评估与优化建议

你的代码已经实现了基础的分页+搜索查询功能,但确实还有不少可以挖掘TanStack Query潜力的地方,以下是具体的优化方向:

1. 移除冗余的前端过滤逻辑

你在后端接口已经传入了searchQuery参数做搜索,但前端又对当前页数据做了二次过滤,这会导致两个问题:

  • 浪费前端计算资源,后端已经返回了过滤后的结果
  • 分页场景下,前端过滤只能覆盖当前页数据,无法得到全量符合条件的结果

直接使用接口返回的data.items即可:

// 移除这段冗余代码
// const filteredItems =
//   data?.items?.filter((user) =>
//     user.name.toLowerCase().includes(search.toLowerCase()) ||
//     user.email.toLowerCase().includes(search.toLowerCase())
//   ) || [];

// 直接使用接口返回的结果
const items = data?.items || [];

2. 完善Query的状态处理

当前只用到了data和isLoading,缺少错误状态处理和加载状态的细分(比如背景刷新的isFetching),这会导致用户在请求失败时没有反馈:

const { 
  data, 
  isLoading, 
  isError, 
  error,
  isFetching // 区分初始加载和后台刷新
} = useQuery({
  queryKey: ["users", currentPage, search], 
  queryFn: () => getUsers(currentPage, search),
});

// 在组件中添加错误提示
if (isError) {
  return <div>加载失败: {(error as Error).message}</div>;
}

3. 优化分页体验:启用keepPreviousData

切换分页时,默认会清空之前的数据并显示加载状态,启用keepPreviousData可以保留上一页的数据,直到新页数据加载完成,提升流畅度:

const { 
  data, 
  isLoading, 
  isError, 
  error,
  isFetching,
  isPreviousData // 标记当前是否使用的是上一页的旧数据
} = useQuery({
  queryKey: ["users", currentPage, search], 
  queryFn: () => getUsers(currentPage, search),
  keepPreviousData: true, // 启用旧数据保留
});

4. 添加请求取消逻辑

使用AbortController在组件卸载或者查询取消时终止未完成的请求,避免无效请求和内存泄漏:

const getUsers = async (page = 1, searchQuery?: string, signal?: AbortSignal): Promise<UserData> => {
  const url = new URL(`fetch-api`);
  url.searchParams.append("curPage", page.toString());
  if (searchQuery) url.searchParams.append("query", searchQuery);

  const response = await fetch(url.toString(), {
    method: "GET",
    headers: { "Content-Type": "application/json" },
    signal, // 传入中止信号
  });

  if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);

  return response.json();
};

// 在useQuery中传递signal
const { data, isLoading } = useQuery({
  queryKey: ["users", currentPage, search], 
  queryFn: ({ signal }) => getUsers(currentPage, search, signal), // 解构获取signal
});

5. 配置缓存策略,减少不必要请求

通过staleTime和cacheTime控制数据的新鲜度和缓存时长,避免每次组件挂载都重新请求:

const { data, isLoading } = useQuery({
  queryKey: ["users", currentPage, search], 
  queryFn: ({ signal }) => getUsers(currentPage, search, signal),
  staleTime: 5 * 60 * 1000, // 5分钟内数据视为新鲜,不发起重复请求
  cacheTime: 30 * 60 * 1000, // 数据缓存30分钟后清除
});

6. 同步搜索状态到URL参数

当前搜索状态存在store中,刷新页面后会丢失,将搜索参数同步到URL可以实现页面刷新、分享链接时保留搜索状态:

import { useRouter } from 'next/navigation'; // 以Next.js为例,根据你的框架调整

export default function Page({ searchParams }: PageProps) {
  const router = useRouter();
  const currentPage = Number(searchParams?.curPage) || 1;
  // 从URL读取初始搜索参数,而不是只依赖store
  const initialSearch = searchParams?.query || "";
  const { search, setSearch } = useUserStore();

  // 组件挂载时同步URL参数到store
  useEffect(() => {
    if (search !== initialSearch) {
      setSearch(initialSearch);
    }
  }, [initialSearch, search, setSearch]);

  // 搜索变化时更新URL参数
  const handleSearchChange = (newSearch: string) => {
    setSearch(newSearch);
    router.push(`/?curPage=1&query=${encodeURIComponent(newSearch)}`, { scroll: false });
  };

  // 更新queryKey,确保和URL参数一致
  const { data, isLoading } = useQuery({
    queryKey: ["users", currentPage, search], 
    queryFn: ({ signal }) => getUsers(currentPage, search, signal),
  });
}

7. 预取下一页数据(可选)

对于分页场景,可以使用prefetchQuery提前加载下一页数据,让用户切换分页时无感知:

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

export default function Page({ searchParams }: PageProps) {
  const queryClient = useQueryClient();
  const currentPage = Number(searchParams?.curPage) || 1;
  const { search } = useUserStore();

  // 预取下一页数据
  useEffect(() => {
    if (data?.hasNextPage) { // 假设接口返回hasNextPage标记是否有下一页
      queryClient.prefetchQuery({
        queryKey: ["users", currentPage + 1, search],
        queryFn: () => getUsers(currentPage + 1, search),
      });
    }
  }, [currentPage, search, data?.hasNextPage, queryClient]);

  // ...其他代码
}

8. 增强类型安全

对currentPage的转换添加有效性校验,避免NaN的情况:

const currentPage = (() => {
  const pageNum = Number(searchParams?.curPage);
  return !isNaN(pageNum) && pageNum >= 1 ? pageNum : 1;
})();

内容的提问来源于stack exchange,提问作者Lem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 19:31:01