在组件层面处理Apollo错误的实现方案问询
嘿,我来给你拆解下在React组件里针对不同Apollo错误场景的响应方法,分情况来看会更清晰:
一、组件内直接捕获查询/突变的错误
Apollo的useQuery和useMutation钩子会直接返回error对象,我们可以根据错误类型在组件内做针对性的UI处理:
1. 处理网络离线/网络错误(红色横幅示例)
通过判断error.networkError来识别网络层面的问题,比如离线、请求超时:
import { useQuery } from '@apollo/client'; import { MY_QUERY } from './queries'; function MyDataComponent() { const { loading, error, data } = useQuery(MY_QUERY); if (loading) return <p>加载中...</p>; // 网络错误场景:显示红色横幅 if (error?.networkError) { return ( <div style={{ backgroundColor: '#dc2626', color: 'white', padding: '1rem', textAlign: 'center' }}> 网络连接失败,请检查你的网络状态 </div> ); } return <div>{data?.content}</div>; }
2. 处理特定业务错误(模态框示例)
后端通常会在error.graphQLErrors里返回带自定义标识的业务错误,我们可以根据extensions.code精准判断:
import { useMutation } from '@apollo/client'; import { DELETE_ITEM } from './mutations'; import ConfirmModal from './ConfirmModal'; function ItemCard({ itemId }) { const [deleteItem, { error, loading }] = useMutation(DELETE_ITEM); const [showModal, setShowModal] = useState(false); const [modalMsg, setModalMsg] = useState(''); // 捕获特定业务错误,触发模态框 useEffect(() => { if (error?.graphQLErrors) { const forbiddenErr = error.graphQLErrors.find(err => err.extensions?.code === 'FORBIDDEN'); if (forbiddenErr) { setModalMsg('你没有权限删除该项目'); setShowModal(true); } } }, [error]); const handleDelete = () => { deleteItem({ variables: { id: itemId } }); }; return ( <> <div className="item-card"> {/* 卡片内容 */} <button onClick={handleDelete} disabled={loading}>删除</button> </div> <ConfirmModal visible={showModal} message={modalMsg} onClose={() => setShowModal(false)} /> </> ); }
3. 触发路由重定向示例
如果遇到需要跳转的错误(比如未登录),可以结合react-router-dom的<Redirect>:
import { useQuery } from '@apollo/client'; import { Redirect } from 'react-router-dom'; import { USER_PROFILE } from './queries'; function ProfilePage() { const { loading, error, data } = useQuery(USER_PROFILE); if (loading) return <p>加载中...</p>; // 未登录错误:重定向到登录页 if (error?.graphQLErrors?.some(err => err.extensions.code === 'UNAUTHENTICATED')) { return <Redirect to="/login" />; } return <div>欢迎,{data?.user?.name}</div>; }
二、全局捕获+组件响应(适合统一处理场景)
如果想全局捕获错误,再分发到组件做不同UI,可以结合React Context和Apollo Error Link:
1. 创建错误上下文
import { createContext, useContext, useState } from 'react'; const ErrorContext = createContext(); export function ErrorProvider({ children }) { const [globalError, setGlobalError] = useState(null); const clearError = () => setGlobalError(null); return ( <ErrorContext.Provider value={{ globalError, setGlobalError, clearError }}> {children} </ErrorContext.Provider> ); } export function useError() { return useContext(ErrorContext); }
2. 配置Apollo Error Link全局捕获错误
import { ApolloClient, InMemoryCache, from } from '@apollo/client'; import { onError } from '@apollo/client/link/error'; import { HttpLink } from '@apollo/client/link/http'; import { useError } from './ErrorContext'; // 注意:如果在组件外初始化Client,需要把setGlobalError通过闭包传入,或者在组件内创建link const createErrorLink = () => { const { setGlobalError } = useError(); return onError(({ graphQLErrors, networkError }) => { if (networkError) { setGlobalError({ type: 'NETWORK', msg: '网络离线,请检查连接' }); } if (graphQLErrors) { graphQLErrors.forEach(err => { if (err.extensions.code === 'REQUIRE_LOGIN') { setGlobalError({ type: 'REDIRECT', target: '/login' }); } else if (err.extensions.code === 'SYSTEM_ERROR') { setGlobalError({ type: 'MODAL', msg: err.message }); } }); } }); }; // 在组件内初始化Client(能访问Context的场景) function ApolloProviderWrapper({ children }) { const errorLink = createErrorLink(); const client = new ApolloClient({ link: from([errorLink, new HttpLink({ uri: '/graphql' })]), cache: new InMemoryCache(), }); return ( <ApolloClientProvider client={client}> {children} </ApolloClientProvider> ); }
3. 全局错误处理组件
import { useError } from './ErrorContext'; import { Redirect } from 'react-router-dom'; import SystemModal from './SystemModal'; function GlobalErrorHandler() { const { globalError, clearError } = useError(); if (!globalError) return null; switch (globalError.type) { case 'NETWORK': return ( <div style={{ background: '#dc2626', color: 'white', padding: '1rem', position: 'fixed', top: 0, width: '100%' }}> {globalError.msg} <button onClick={clearError} style={{ marginLeft: '1rem' }}>关闭</button> </div> ); case 'MODAL': return <SystemModal msg={globalError.msg} onClose={clearError} />; case 'REDIRECT': clearError(); return <Redirect to={globalError.target} />; default: return null; } }
三、关键注意点
- 区分networkError和graphQLErrors:前者是网络层面的故障,后者是后端返回的业务/权限错误。
- 自定义错误码:建议后端在
error.extensions里返回自定义code,方便前端精准判断错误类型。 - 清理错误状态:处理完错误后记得调用清除方法,避免错误UI一直停留。
内容的提问来源于stack exchange,提问作者sh03
相关产品推荐
相关产品推荐

