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

NextJS中getServerSideProps与react-query预取数据及数据丢失问题解决

问题分析与解决方案

核心问题原因

  1. 缺少全局React Query Provider:未在_app.tsx中配置全局的QueryClientProvider,导致每次页面跳转都会创建全新的QueryClient实例,之前的缓存数据被清空,DevTools中自然看不到历史数据。
  2. 查询键与数据类型不一致:分页参数可能为字符串类型,导致查询键(如['notices', '2'])与预期的数字键(['notices', 2])不匹配,缓存无法正确关联。

解决步骤

1. 配置全局React Query Provider

在pages/_app.tsx中添加全局QueryClient实例,确保整个应用共享同一缓存:

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

// 创建全局QueryClient实例
const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 30 * 1000, // 按需设置,减少重复请求
      retry: 1,
    },
  },
});

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <QueryClientProvider client={queryClient}>
      <Component {...pageProps} />
      <ReactQueryDevtools initialIsOpen={false} />
    </QueryClientProvider>
  );
}

2. 优化公告页面代码

调整getServerSideProps和useQuery逻辑,确保查询键一致、缓存正确存入,并实现预取:

// page/notice/index.tsx
export async function getServerSideProps(context) {
  // 处理page为空的情况,默认设为1,同时转为数字类型
  const currentPage = Number(context.query.page) || 1;
  const res = await getNotices(currentPage);
  const data = res.data;

  return {
    props: { 
      result: data,
      currentPage, // 明确传递数字类型的当前页
    },
  };
}

export async function getNotices(page: number) {
  return await api.get(`/admin/notice?page=${page}`);
}

export default function Notice({ result, currentPage }) {
  const queryClient = useQueryClient();

  const { data } = useQuery(
    ['notices', currentPage], // 数字类型的查询键,确保一致性
    () => getNotices(currentPage),
    {
      initialData: result, // 将SSR获取的数据作为初始值存入缓存
      select: (response) => response.data, // 匹配initialData的结构
      staleTime: 30 * 1000,
    },
  );

  // 预取下一页数据(避免重复预取)
  useEffect(() => {
    const nextPage = currentPage + 1;
    // 先检查缓存是否已有数据,没有再发起预取
    if (!queryClient.getQueryData(['notices', nextPage])) {
      queryClient.prefetchQuery(
        ['notices', nextPage],
        () => getNotices(nextPage),
        { staleTime: 30 * 1000 },
      );
    }

    // 可选:预取上一页数据
    if (currentPage > 1 && !queryClient.getQueryData(['notices', currentPage - 1])) {
      queryClient.prefetchQuery(
        ['notices', currentPage - 1],
        () => getNotices(currentPage - 1),
        { staleTime: 30 * 1000 },
      );
    }
  }, [currentPage, queryClient]);

  // 你的渲染逻辑...
}

3. 分页跳转优化(可选)

保持原有的router.push逻辑,确保传递数字类型的page参数:

// 分页按钮点击事件示例
const handlePageChange = (newPage: number) => {
  router.push(`/notice?page=${newPage}`);
};

效果验证

完成上述配置后:

  • 点击分页按钮时,getServerSideProps会正常获取当前页数据,同时React Query会将该数据存入全局缓存。
  • 预取的下一页数据会被缓存,用户点击下一页时可直接从缓存读取,提升体验。
  • React Query DevTools中能看到所有已缓存的分页数据,不会再出现数据消失的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 16:30:58