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

在组件层面处理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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:13:56