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

React中不同响应状态的错误处理优化及Hook调用问题咨询

解决方案:React + Spring Boot 全栈应用错误处理优化

一、优雅解析并展示不同错误响应

可以封装统一的错误解析工具函数,替代零散的可选链判断,让错误处理逻辑复用且更清晰。

实现错误解析工具函数

创建utils/errorParser.js:

export const parseErrorResponse = (error) => {
  // 处理无响应的网络异常
  if (!error.response) {
    return { message: '网络异常,请检查连接后重试' };
  }

  const { status, data } = error.response;
  
  switch (status) {
    // 凭证错误(500)
    case 500:
      return { message: data?.message || '服务器认证错误' };
    // 字段验证错误(422)
    case 422:
      const errorDetails = data?.errors?.map(err => `${err.field}:${err.message}`).join(';');
      return {
        message: data?.message || '表单验证失败',
        details: errorDetails
      };
    // 其他状态码
    default:
      return { message: data?.message || `请求失败,状态码:${status}` };
  }
};

在组件中使用

在请求的catch块中调用该函数,直接获取格式化后的错误信息:

import { parseErrorResponse } from '../utils/errorParser';
import { useSnackbar } from 'notistack';

const LoginComponent = () => {
  const { enqueueSnackbar } = useSnackbar();

  const handleLogin = async (credentials) => {
    try {
      await axios.post('/api/auth/login', credentials);
      // 登录成功逻辑
    } catch (error) {
      const errorInfo = parseErrorResponse(error);
      // 拼接详情并展示
      const displayMessage = errorInfo.details ? `${errorInfo.message}:${errorInfo.details}` : errorInfo.message;
      enqueueSnackbar(displayMessage, { variant: 'error' });
    }
  };

  return (/* 组件内容 */);
};

二、Service层调用enqueueSnackbar的解决方案

由于React Hook只能在组件或自定义Hook中调用,不能直接在Service层使用useSnackbar,推荐以下三种方案:

方案1:将enqueueSnackbar作为参数传入Service方法

Service层函数接收回调函数,组件调用时传入enqueueSnackbar:

// services/authService.js
import axios from 'axios';
import { parseErrorResponse } from '../utils/errorParser';

export const login = async (credentials, showErrorSnackbar) => {
  try {
    const response = await axios.post('/api/auth/login', credentials);
    return response.data;
  } catch (error) {
    const errorInfo = parseErrorResponse(error);
    const displayMessage = errorInfo.details ? `${errorInfo.message}:${errorInfo.details}` : errorInfo.message;
    showErrorSnackbar(displayMessage, { variant: 'error' });
    throw error; // 抛出错误让组件处理后续逻辑
  }
};

组件中调用:

import { login } from '../services/authService';
import { useSnackbar } from 'notistack';

const LoginComponent = () => {
  const { enqueueSnackbar } = useSnackbar();

  const handleLogin = async (credentials) => {
    try {
      await login(credentials, enqueueSnackbar);
      // 登录成功跳转等逻辑
    } catch (error) {
      // 可选:额外的错误处理
    }
  };

  return (/* 组件内容 */);
};

方案2:用自定义Hook封装Service逻辑

将Service的请求逻辑放到自定义Hook中,合法使用useSnackbar:

// hooks/useAuthService.js
import { useSnackbar } from 'notistack';
import axios from 'axios';
import { parseErrorResponse } from '../utils/errorParser';

export const useAuthService = () => {
  const { enqueueSnackbar } = useSnackbar();

  const login = async (credentials) => {
    try {
      const response = await axios.post('/api/auth/login', credentials);
      return response.data;
    } catch (error) {
      const errorInfo = parseErrorResponse(error);
      const displayMessage = errorInfo.details ? `${errorInfo.message}:${errorInfo.details}` : errorInfo.message;
      enqueueSnackbar(displayMessage, { variant: 'error' });
      throw error;
    }
  };

  return { login };
};

组件中使用:

import { useAuthService } from '../hooks/useAuthService';

const LoginComponent = () => {
  const { login } = useAuthService();

  const handleLogin = async (credentials) => {
    try {
      await login(credentials);
      // 登录成功逻辑
    } catch (error) {
      // 可选:额外处理
    }
  };

  return (/* 组件内容 */);
};

方案3:全局状态管理(适合复杂应用)

通过Zustand、Redux等全局状态库,将错误信息存入全局状态,再由全局Snackbar组件监听状态变化自动显示提示:

// store/snackbarStore.js(Zustand示例)
import { create } from 'zustand';

export const useSnackbarStore = create((set) => ({
  pendingSnackbar: null,
  showSnackbar: (message, options) => set({ pendingSnackbar: { message, options } }),
  clearPending: () => set({ pendingSnackbar: null })
}));

全局Snackbar组件:

// components/GlobalSnackbar.js
import { useEffect } from 'react';
import { useSnackbar } from 'notistack';
import { useSnackbarStore } from '../store/snackbarStore';

const GlobalSnackbar = () => {
  const { pendingSnackbar, clearPending } = useSnackbarStore();
  const { enqueueSnackbar } = useSnackbar();

  useEffect(() => {
    if (pendingSnackbar) {
      enqueueSnackbar(pendingSnackbar.message, pendingSnackbar.options);
      clearPending();
    }
  }, [pendingSnackbar, enqueueSnackbar, clearPending]);

  return null;
};

export default GlobalSnackbar;

在App.js中挂载该组件:

import GlobalSnackbar from './components/GlobalSnackbar';

function App() {
  return (
    <div className="App">
      <GlobalSnackbar />
      {/* 其他路由/组件 */}
    </div>
  );
}

Service层调用:

// services/authService.js
import axios from 'axios';
import { parseErrorResponse } from '../utils/errorParser';
import { useSnackbarStore } from '../store/snackbarStore';

export const login = async (credentials) => {
  try {
    const response = await axios.post('/api/auth/login', credentials);
    return response.data;
  } catch (error) {
    const errorInfo = parseErrorResponse(error);
    const displayMessage = errorInfo.details ? `${errorInfo.message}:${errorInfo.details}` : errorInfo.message;
    useSnackbarStore.getState().showSnackbar(displayMessage, { variant: 'error' });
    throw error;
  }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 01:17:55