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

如何在next-mdx-remote的MDX组件中使用react-query?

解决next-mdx-remote中react-query找不到QueryClient的问题

这个问题的核心其实和next-mdx-remote的渲染机制有关——你用的renderToString是同步的服务端渲染方法,它执行的时候完全脱离了_app.js里QueryClientProvider的上下文环境。

简单说:你传入components里的那些组件,在renderToString执行时是被直接编译成字符串的,并没有被包裹在你全局配置的QueryClientProvider里,所以组件里的useQuery自然找不到对应的QueryClient实例。

下面给你几个可行的解决方案,按推荐程度排序:

方案1:页面层预取数据,通过props传给MDX组件(最推荐)

把数据请求逻辑从MDX组件里抽出来,放到页面的getStaticProps/getServerSideProps中,用react-query预取数据后,通过props传递给MDX组件。这样MDX组件只负责展示,完全避开上下文问题,还能利用Next.js的预渲染优化性能。

示例代码:

// 页面组件
import { dehydrate, QueryClient } from '@tanstack/react-query';
import { renderToString } from 'next-mdx-remote/render-to-string';
import { MDXRemote } from 'next-mdx-remote';

// 你的MDX组件,现在只接收props展示数据
const DataDisplayComponent = ({ data }) => {
  return <div>{JSON.stringify(data)}</div>;
};

export async function getStaticProps() {
  // 1. 创建临时QueryClient预取数据
  const queryClient = new QueryClient();
  await queryClient.prefetchQuery({
    queryKey: ['your-target-data'],
    queryFn: () => fetch('https://api.example.com/data').then(res => res.json()),
  });

  // 2. 渲染MDX内容,给组件预留props位
  const mdxContent = `# 这是MDX内容
<DataDisplayComponent />`;
  const mdxSource = await renderToString(mdxContent, {
    components: { DataDisplayComponent },
  });

  // 3. 返回预取的状态和MDX源
  return {
    props: {
      mdxSource,
      dehydratedState: dehydrate(queryClient),
    },
  };
}

export default function MdxPage({ mdxSource, dehydratedState }) {
  // 从react-query的缓存中取出数据,传给MDX组件
  const data = useQuery(['your-target-data']).data;

  return (
    <MDXRemote 
      {...mdxSource} 
      components={{
        DataDisplayComponent: () => <DataDisplayComponent data={data} />
      }} 
    />
  );
}

方案2:给MDX组件手动包裹QueryClientProvider

如果必须在MDX组件里用useQuery,可以在renderToString的时候,给需要使用react-query的组件手动套一层QueryClientProvider,同时要记得同步服务端和客户端的缓存状态:

// 页面的getStaticProps中
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { dehydrate } from '@tanstack/react-query';
import { renderToString } from 'next-mdx-remote/render-to-string';

export async function getStaticProps() {
  const queryClient = new QueryClient();
  
  // 渲染MDX时,给目标组件包裹QueryClientProvider
  const mdxSource = await renderToString(yourMdxContent, {
    components: {
      YourQueryComponent: (props) => (
        <QueryClientProvider client={queryClient}>
          <YourQueryComponent {...props} />
        </QueryClientProvider>
      ),
    },
  });

  return {
    props: {
      mdxSource,
      dehydratedState: dehydrate(queryClient),
    },
  };
}

这种方式要注意:服务端创建的QueryClient状态必须通过dehydrate传递给客户端,否则客户端会重新请求一次数据,造成冗余请求。

额外注意事项

  • 如果你的useQuery的queryFn涉及浏览器专属API(比如window),一定要加判断,避免服务端渲染时报错:
    useQuery({
      queryKey: ['data'],
      queryFn: () => fetchData(),
      enabled: typeof window !== 'undefined', // 只在客户端执行请求
    });
    
  • 确保_app.js里的Hydrate组件正确包裹了所有页面,这样客户端才能复用服务端预取的缓存数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 15:42:29