Apollo Client页面刷新后Pending请求计数失效的修复咨询
解决方案:修复页面刷新后Pending请求计数失效问题
问题根源
你当前的实现存在三个核心问题:
- 动态修改Apollo Client链接时机错误:在组件内部通过
client.setLink添加middlewareLink,页面刷新后部分请求(比如应用初始化时的请求)可能在组件挂载前就已发送,不会经过新创建的middlewareLink,导致计数不更新。 - 未处理完整请求生命周期:只在请求完成时减少计数,未处理请求错误、订阅取消(比如组件卸载导致请求中止)的场景,会导致计数不准确。
- 轮询方式低效:用
setInterval轮询获取计数不是响应式的最佳实践,容易出现状态不同步。
方案1:基于RxJS的响应式实现(推荐)
利用RxJS的BehaviorSubject实现计数的响应式更新,确保所有请求都经过middlewareLink:
1. 重构Pending请求链接
import { ApolloLink } from '@apollo/client'; import { BehaviorSubject } from 'rxjs'; export const createPendingRequestsLink = () => { const pendingRequests$ = new BehaviorSubject(0); let pendingRequests = 0; const middlewareLink = new ApolloLink((operation, forward) => { // 请求开始时增加计数 pendingRequests++; pendingRequests$.next(pendingRequests); return new Observable((observer) => { const subscription = forward(operation).subscribe({ next: (result) => observer.next(result), error: (error) => { observer.error(error); // 请求出错时减少计数 pendingRequests--; pendingRequests$.next(pendingRequests); }, complete: () => { observer.complete(); // 请求完成时减少计数 pendingRequests--; pendingRequests$.next(pendingRequests); }, }); return () => { // 请求被取消时减少计数 pendingRequests--; pendingRequests$.next(pendingRequests); subscription?.unsubscribe(); }; }); }); return { middlewareLink, pendingRequests$ }; };
2. 初始化Apollo Client时注入链接
在全局的Apollo Client初始化文件中(比如apolloClient.js),把middlewareLink加入链接链:
import { ApolloClient, InMemoryCache, ApolloLink, HttpLink } from '@apollo/client'; import { createPendingRequestsLink } from './path-to-your-link-file'; const { middlewareLink, pendingRequests$ } = createPendingRequestsLink(); const httpLink = new HttpLink({ uri: '/your-graphql-endpoint' }); const client = new ApolloClient({ link: ApolloLink.from([middlewareLink, httpLink]), cache: new InMemoryCache(), }); // 导出供组件使用 export { client, pendingRequests$ };
3. 组件中订阅计数变化
import { useState, useEffect } from 'react'; import { pendingRequests$ } from './apolloClient'; function YourComponent() { const [pendingCount, setPendingCount] = useState(0); useEffect(() => { // 订阅计数变化,自动更新组件状态 const subscription = pendingRequests$.subscribe(count => setPendingCount(count)); return () => subscription.unsubscribe(); }, []); return ( <div> {/* 根据计数显示加载动画 */} {pendingCount > 0 && <div className="spinner">加载中...</div>} {/* 你的组件内容 */} </div> ); }
方案2:纯React Context实现(无需RxJS)
如果不想引入RxJS,可以用React Context管理计数状态:
1. 创建Pending请求Context
import { createContext, useContext, useState } from 'react'; const PendingRequestsContext = createContext(); export function PendingRequestsProvider({ children }) { const [pendingCount, setPendingCount] = useState(0); return ( <PendingRequestsContext.Provider value={{ pendingCount, setPendingCount }}> {children} </PendingRequestsContext.Provider> ); } export function usePendingRequests() { return useContext(PendingRequestsContext); }
2. 重构Pending请求链接
import { ApolloLink } from '@apollo/client'; export const createPendingRequestsLink = (setPendingCount) => { let pendingRequests = 0; return new ApolloLink((operation, forward) => { pendingRequests++; setPendingCount(pendingRequests); return new Observable((observer) => { const subscription = forward(operation).subscribe({ next: (result) => observer.next(result), error: (error) => { observer.error(error); pendingRequests--; setPendingCount(pendingRequests); }, complete: () => { observer.complete(); pendingRequests--; setPendingCount(pendingRequests); }, }); return () => { pendingRequests--; setPendingCount(pendingRequests); subscription?.unsubscribe(); }; }); }); };
3. 全局注入Client和Context
import { ApolloProvider, ApolloClient, InMemoryCache, ApolloLink, HttpLink } from '@apollo/client'; import { PendingRequestsProvider, usePendingRequests } from './path-to-context'; import { createPendingRequestsLink } from './path-to-link'; function ApolloWrapper({ children }) { const { setPendingCount } = usePendingRequests(); const httpLink = new HttpLink({ uri: '/your-graphql-endpoint' }); const middlewareLink = createPendingRequestsLink(setPendingCount); const client = new ApolloClient({ link: ApolloLink.from([middlewareLink, httpLink]), cache: new InMemoryCache(), }); return <ApolloProvider client={client}>{children}</ApolloProvider>; } function App() { return ( <PendingRequestsProvider> <ApolloWrapper> {/* 你的应用组件 */} </ApolloWrapper> </PendingRequestsProvider> ); }
4. 组件中使用计数
import { usePendingRequests } from './path-to-context'; function YourComponent() { const { pendingCount } = usePendingRequests(); return ( <div> {pendingCount > 0 && <div className="spinner">加载中...</div>} {/* 你的组件内容 */} </div> ); }
旧问题补充说明
针对你最初的需求(监控所有Pending请求),上面的方案已经完全覆盖:通过Apollo Link拦截所有请求的生命周期,统一维护Pending请求计数,无需再单独监控单个watchQuery的networkStatus。
内容的提问来源于stack exchange,提问作者Alberto Manuguerra
相关产品推荐
相关产品推荐

