React项目使用多个Apollo Client实例遇无限渲染如何解决
多Apollo Client实例接入解决方案
问题根因
你遇到的无限渲染和重复请求问题,核心原因是client实例在组件渲染时被反复重建:如果new ApolloClient的逻辑写在组件函数内部,每次组件渲染都会生成一个全新的client对象,useQuery检测到client参数变化后会重新触发请求,进而导致组件再次渲染,形成死循环。
此外你提供的代码示例中useQuery的配置项client: client1后缺少逗号,也会导致语法异常。
可行实现方案
方案1:全局声明client实例(无动态配置时优先使用)
如果两个client的配置是固定的,不需要依赖组件内的状态/Props,直接将实例定义在组件外部,或者单独抽离为配置文件导出即可:
// apollo/clients.js 单独配置文件 import { ApolloClient, InMemoryCache, from } from '@apollo/client'; export const client1 = new ApolloClient({ link: from([authLink, errorLink, httpLink1]), cache: new InMemoryCache(), }); export const client2 = new ApolloClient({ link: from([authLink, errorLink, httpLink2]), cache: new InMemoryCache(), });
组件内使用时直接传入实例,不会触发重复重建:
import { client1, client2 } from './apollo/clients'; function MyComponent() { // 调用第一个API const { data: data1 } = useQuery(QUERY_1, { client: client1, variables: vars1 }); // 调用第二个API const { data: data2 } = useQuery(QUERY_2, { client: client2, variables: vars2 }); }
方案2:useMemo缓存实例(配置依赖组件内状态时使用)
如果client的配置需要依赖组件内的状态(比如用户token动态生成),用useMemo包裹实例创建逻辑,仅当依赖项变化时才重建client:
function MyComponent({ userToken }) { const client1 = useMemo(() => { const authLink = setContext((_, { headers }) => { return { headers: { ...headers, authorization: `Bearer ${userToken}` } } }); return new ApolloClient({ link: from([authLink, errorLink, httpLink1]), cache: new InMemoryCache(), }); // 仅当userToken变化时才重建client }, [userToken]); const { data } = useQuery(GET_COLLECTIONS, { client: client1, variables: variables }); }
方案3:嵌套ApolloProvider(多组件共用第二个client时使用)
如果有多个组件需要使用第二个client,可以嵌套ApolloProvider,内层Provider注入第二个client,内层组件默认使用该实例,无需每次传参:
// 根组件注入默认client function App() { return ( <ApolloProvider client={client1}> {/* 大部分默认用client1的组件 */} <ComponentA /> {/* 需要用client2的组件区域 */} <ApolloProvider client={client2}> <ComponentB /> {/* ComponentB内部的useQuery默认使用client2 */} {/* 需要同时调用两个API的组件,手动指定client即可 */} <ComponentC /> </ApolloProvider> </ApolloProvider> ) }
可选优化:单实例拆分链路(无缓存隔离需求时使用)
如果只是需要调用两个不同的GraphQL端点,不需要隔离缓存、中间件配置,不用创建两个client实例,通过split方法按请求特征分发到不同的http链路即可:
import { split, HttpLink } from '@apollo/client'; import { getMainDefinition } from '@apollo/client/utilities'; const api1Link = new HttpLink({ uri: 'https://api1.example.com/graphql' }); const api2Link = new HttpLink({ uri: 'https://api2.example.com/graphql' }); const splitLink = split( // 自定义判断条件,比如查询名带Api2前缀的走第二个接口 ({ query }) => { const definition = getMainDefinition(query); return definition.kind === 'OperationDefinition' && definition.name?.value.startsWith('Api2'); }, api2Link, api1Link ); // 全局只有一个client实例 const client = new ApolloClient({ link: splitLink, cache: new InMemoryCache() });
内容的提问来源于stack exchange,提问作者yeomane
相关产品推荐
相关产品推荐

