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

Next.js中用Link重入同URL时prefetchQuery不触发SSR数据获取

Next.js + React Query SSR 路由跳转数据异常问题解答

问题1:SSR场景下使用Next.js的Link组件是否属于不当操作?首屏SSR后后续是不是转为CSR?

完全没问题,Next.js的Link组件就是官方推荐的客户端路由跳转方案。首屏通过SSR渲染后,后续所有通过Link的跳转都会走客户端路由(CSR),不会触发全页面刷新,这是正常且优化的行为,不属于不当操作。

问题2:为何id参数有效时useQuery仍不获取数据?

从你提供的代码来看,主要问题出在缓存匹配和客户端请求触发这两个点上:

  1. 手动修改 dehydratedState 的风险
    你在getServerSideProps里手动过滤并修改了脱水后的查询缓存,只保留了当前id的单个查询数据。但通过Link跳转时是客户端路由,不会重新执行getServerSideProps预取新数据,此时React Query会先尝试从客户端缓存中读取对应查询键的数据。如果跳转的是新id,缓存里没有匹配的键,就需要发起新请求,但如果请求没触发,大概率是下面的原因:

  2. 查询键不匹配
    检查SUPPORT_QUERY.useGetBoardContent内部的查询键是否和预取时的["support", "faq", id]完全一致。如果查询键生成逻辑不同(比如多了/少了参数、category的拼写不一致),React Query会认为是新查询,要么找不到缓存,要么如果配置了enabled: false就不会发起请求。

  3. 客户端请求未触发
    默认情况下useQuery会自动发起请求,但如果你的自定义hook里设置了enabled条件(比如依赖某个状态),或者请求本身有错误被静默处理了,都会导致数据一直是undefined。可以在浏览器DevTools的Network面板查看是否有对应的API请求发出,或者在useGetBoardContent里添加错误日志排查。

  4. 数据结构修改导致的缓存问题
    你在脱水时把targetQueryState.data.data赋值给了state.data,这会改变缓存中数据的结构。如果客户端useQuery期望的是原始接口返回的结构,可能会导致解析错误,进而无法正确渲染数据。建议尽量不要手动修改脱水后的查询数据结构,保持预取和客户端查询的数据结构一致。

修复建议

  • 移除getServerSideProps中手动修改dehydratedState.queries的逻辑,直接返回dehydrate(queryClient)生成的完整脱水状态,避免破坏缓存结构。
  • 确保useGetBoardContent的查询键和预取时的["support", category, id]完全匹配(category要正确传入)。
  • 在客户端添加错误处理,比如通过useQuery的error属性排查请求失败原因。

相关代码参考

页面组件(FAQDetailPage)

// PAGE, I pass the query string to the <FAQContent /> Component

export default function FAQDetailPage({ id }: { id: string }) {

  return <FAQContent id={id} category="faq" />;
}

export async function getServerSideProps({
  query: { id },
}: { query: { id: string } }) {
  const queryClient = new QueryClient();

  try {
    await queryClient.prefetchQuery(["support", "faq", id], () => {
      return sendRequest({
        method: "get",
        path: `/article/${id}`,
        apiType: "Contents",
      });
    });
  } catch (e) {
    return {
      redirect: {
        destination: "/",
        permanent: false,
      },
    };
  }

  // 直接返回完整的脱水状态,不要手动修改
  return {
    props: {
      dehydratedState: dehydrate(queryClient),
      id,
    },
  };
}

FAQContent组件

// Get id and category from the page, and pass to <SupportBoardContent />

export default function FAQContent({
  id,
  category,
}: { id: string; category: string }) {

  return <SupportBoardContent id={id} category={category} />;
}

SupportBoardContent组件

// Here is the component where hydrate the dehydrated query

export default function SupportBoardContent({
  id,
  category,
}: { id: string; category: string }) {
  const router = useRouter();

  const { pathname } = router;

  // This is a hook for useQuery.
  const {
    data: boardContent,
    error,
    ResponseHandler: ResponseHandlerOfGetBoardContent,
  } = SUPPORT_QUERY.useGetBoardContent({
    id,
    category,
  });

  // 添加错误日志排查
  if (error) console.error("Query error:", error);

  const { title, contents, memo, videoURL, articleType } = 
    boardContent?.targetArticle || {};


  return (
    <SupportLayout>
      {/* 可以添加加载状态提示 */}
      {!boardContent && !error && <div>加载中...</div>}
      {boardContent && (
        <CommonStyled.mainContainer>
          <SupportBreadcrumb title={title} />
          <BoardPageTitle titleStyle="default">
            {getArticleBoardTypeString(articleType)}
          </BoardPageTitle>
          <BoardContent
            title={title}
            annotation={memo}
            desc={contents}
            videoURL={videoURL}
          />
        </CommonStyled.mainContainer>
      )}
      {error && <div>加载失败,请重试</div>}
    </SupportLayout>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 12:59:13