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

Apollo useQuery仅执行一次:组件重渲染时跳过请求的解决办法

解决Next.js + GraphQL仅获取一次数据并复用的思路

一、利用Apollo Client的缓存策略(最直接)

Apollo Client默认会缓存查询结果,通过设置fetchPolicy可以强制优先使用缓存,避免重复请求:

  • cache-first:优先从缓存读取数据,缓存不存在时才发起请求(推荐)
  • cache-only:仅从缓存读取,适合确定数据已存在的场景

示例代码:

import { useQuery } from '@apollo/client';
import YOUR_QUERY from './queries/yourQuery';

function YourComponent() {
  const { data, loading, error } = useQuery(YOUR_QUERY, {
    fetchPolicy: 'cache-first',
  });

  if (loading) return <div>加载中...</div>;
  if (error) return <div>出错了</div>;

  return <div>{JSON.stringify(data)}</div>;
}

二、将查询放在不会重渲染的根组件中

Next.js的_app.js(Page Router)或layout.js(App Router)是全局根组件,只会在应用初始化或页面切换时渲染,不会因子组件重渲染而重新执行查询。可以在这里获取数据后通过props传递给子组件:

Page Router 示例(_app.js):

import { useQuery } from '@apollo/client';
import YOUR_QUERY from './queries/yourQuery';

function MyApp({ Component, pageProps }) {
  const { data, loading, error } = useQuery(YOUR_QUERY, {
    fetchPolicy: 'cache-first',
  });

  if (loading) return <div>全局加载中...</div>;
  if (error) return <div>全局数据请求失败</div>;

  return <Component {...pageProps} sharedData={data} />;
}

export default MyApp;

子组件直接使用props中的sharedData即可,无需再发起查询。

三、用React Context封装全局数据

将查询逻辑封装在Context Provider中,Provider仅在初始化时执行一次查询,整个应用的组件都可以通过Context复用数据:

1. 创建DataContext:

// src/contexts/DataContext.js
import { createContext, useContext } from 'react';
import { useQuery } from '@apollo/client';
import YOUR_QUERY from '../queries/yourQuery';

const DataContext = createContext();

export function DataProvider({ children }) {
  const { data, loading, error } = useQuery(YOUR_QUERY, {
    fetchPolicy: 'cache-first',
  });

  return (
    <DataContext.Provider value={{ data, loading, error }}>
      {children}
    </DataContext.Provider>
  );
}

export function useData() {
  return useContext(DataContext);
}

2. 在根组件中包裹Provider:

// src/pages/_app.js(Page Router)
import { DataProvider } from '../contexts/DataContext';

function MyApp({ Component, pageProps }) {
  return (
    <DataProvider>
      <Component {...pageProps} />
    </DataProvider>
  );
}

3. 子组件中使用:

import { useData } from '../contexts/DataContext';

function ChildComponent() {
  const { data, loading, error } = useData();

  if (loading) return <div>加载中...</div>;
  if (error) return <div>出错了</div>;

  return <div>{JSON.stringify(data)}</div>;
}

四、利用Next.js的静态/服务器端渲染预取数据

如果数据不需要在客户端动态更新,可以在构建时(静态生成)或服务器端请求数据,直接传递给组件,客户端完全复用预取的数据:

Page Router(getStaticProps):

// src/pages/index.js
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

// 定义查询
const YOUR_QUERY = gql`
  query GetData {
    // 你的查询内容
  }
`;

export async function getStaticProps() {
  // 创建临时Apollo客户端
  const client = new ApolloClient({
    uri: '你的GraphQL服务器地址',
    cache: new InMemoryCache(),
  });

  const { data } = await client.query({ query: YOUR_QUERY });

  return {
    props: { initialData: data },
    revalidate: false, // 禁用增量静态再生,永远使用初始数据
  };
}

export default function Home({ initialData }) {
  // 直接使用预取的initialData,无需useQuery
  return <div>{JSON.stringify(initialData)}</div>;
}

App Router(async组件):

// src/app/page.js
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

const YOUR_QUERY = gql`
  query GetData {
    // 你的查询内容
  }
`;

async function fetchInitialData() {
  const client = new ApolloClient({
    uri: '你的GraphQL服务器地址',
    cache: new InMemoryCache(),
  });
  const { data } = await client.query({ query: YOUR_QUERY });
  return data;
}

export default async function Home() {
  const initialData = await fetchInitialData();
  return <div>{JSON.stringify(initialData)}</div>;
}

补充:关于useLazyQuery的正确用法

如果一定要用useLazyQuery,需要把触发逻辑放在useEffect中,确保仅执行一次:

import { useLazyQuery } from '@apollo/client';
import YOUR_QUERY from './queries/yourQuery';
import { useEffect } from 'react';

function YourComponent() {
  const [getData, { data, loading, error }] = useLazyQuery(YOUR_QUERY);

  useEffect(() => {
    getData(); // 仅在组件挂载时触发一次
  }, []);

  // 后续渲染直接复用data
  if (loading) return <div>加载中...</div>;
  if (error) return <div>出错了</div>;

  return <div>{JSON.stringify(data)}</div>;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:15:41