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

如何用React Hooks统一处理Apollo客户端的错误?

统一处理Apollo GraphQL的Mutation/Query错误(无需Redux)

完全不用引入Redux就能解决这个重复编码的问题!核心思路是让Apollo的errorLink能访问到你ErrorProvider里的状态更新方法,同时避开Hooks只能在组件内调用的限制,具体可以这么做:

第一步:改造ErrorProvider,暴露状态更新的引用

修改你的ErrorProvider,用useRef来保存dispatch函数,然后导出一个方法让外部能获取到这个引用:

// ErrorProvider.jsx
import { createContext, useContext, useReducer, useRef, useEffect } from 'react';

const ErrorContext = createContext();

const errorReducer = (state, action) => {
  switch (action.type) {
    case 'SET_ERROR':
      return { ...state, error: action.payload };
    case 'CLEAR_ERROR':
      return { ...state, error: null };
    default:
      return state;
  }
};

export const ErrorProvider = ({ children }) => {
  const [state, dispatch] = useReducer(errorReducer, { error: null });
  const dispatchRef = useRef();

  // 每次dispatch更新时,同步更新ref的current值
  useEffect(() => {
    dispatchRef.current = dispatch;
  }, [dispatch]);

  // 导出获取dispatch的方法,供外部link调用
  export const getErrorDispatch = () => dispatchRef.current;

  return (
    <ErrorContext.Provider value={{ ...state, dispatch }}>
      {children}
    </ErrorContext.Provider>
  );
};

export const useError = () => useContext(ErrorContext);

第二步:在Apollo的errorLink里统一处理错误

回到app.jsx,导入刚才导出的getErrorDispatch,然后在onError回调里调用它来分发错误:

// app.jsx
import { onError } from 'apollo-link-error';
import { ApolloLink, split } from '@apollo/client';
import { getErrorDispatch } from './ErrorProvider';

// ...你的httpLink、split逻辑
const httpLink = /* 你的httpLink配置 */;
const errorLink = onError((err) => {
  console.log(err);
  // 获取ErrorProvider的dispatch方法
  const dispatch = getErrorDispatch();
  if (dispatch) {
    // 根据你的需求格式化错误,比如区分GraphQL错误和网络错误
    const errorPayload = {
      graphQLErrors: err.graphQLErrors,
      networkError: err.networkError,
      message: err.message
    };
    dispatch({ type: 'SET_ERROR', payload: errorPayload });
  }
});

// 注意link顺序:errorLink要放在前面,才能捕获后续link的错误
const client = new ApolloClient({
  link: ApolloLink.from([errorLink, split(/* 你的split条件 */, httpLink)]),
  // ...其他配置(cache等)
});

// 保持你的Provider嵌套顺序即可
<ApolloProvider client={client}>
  <ErrorProvider>
    {/* 你的应用组件 */}
  </ErrorProvider>
</ApolloProvider>

为什么这个方案可行?

  • useRef的current属性是可变的,不会触发组件重渲染,我们通过它把组件内的dispatch函数传递给了组件外的Apollo link。
  • 只要ErrorProvider完成渲染,dispatchRef.current就会被赋值,后续所有的Query/Mutation错误都会被errorLink捕获并自动分发到你的全局错误状态里,再也不用每个请求都写onError回调了。

额外注意点

  • Link顺序很重要:errorLink必须放在httpLink(或其他请求link)的前面,这样才能捕获到请求过程中产生的错误。
  • 处理初始化场景:如果你的应用在ErrorProvider渲染前就发起了请求(比如SSR场景),dispatch可能还未赋值,所以要加if (dispatch)的判断避免报错。
  • 错误格式化:你可以在errorLink里根据err.graphQLErrors和err.networkError区分不同类型的错误,再把格式化后的信息传给dispatch,让错误状态更易用。

为什么不需要Redux?

你的ErrorProvider本身已经是一个轻量的全局状态容器了,通过useRef传递dispatch的方式已经完美解决了Apollo link访问组件内状态的问题,完全没必要引入Redux增加复杂度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 08:27:30