如何用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
相关产品推荐
相关产品推荐

