🛠️ React 项目中接口错误提示的三种实践方案:UI 直出 vs 全局 Handler vs EventBus
在实际开发中,我们经常需要在请求失败时展示统一的错误提示。无论是
axios
、fetch
,只要涉及封装请求,就绕不开"请求错误如何提示用户"的话题。本文基于本人项目实战,梳理了三种常见实现方案,从简单到高级一应俱全,供你按需选用。
✅ 场景说明
- 使用 React(任意框架均可类比)
- 使用 axios 封装请求
- 错误码中有业务错误(如
code: 1
,而非 HTTP 错误) - 希望在请求失败时展示 toast 或其他提示
🔧 三种方案概览
方案 | 简介 | 优点 | 缺点 | 适用项目 |
---|---|---|---|---|
方案一 | 直接在 axios 中调用 UI | 快速简单 | UI 耦合严重,不易维护 | demo、PoC、小项目 |
方案二 | 抽出全局 UI handler 注入 | UI 解耦、可控性强 | 初始化要求高 | 中小项目 |
方案三 | 使用 EventBus 派发事件 | 最灵活、可统一处理复杂错误码 | 代码复杂度略高 | 中大型项目、多人协作项目 |
🚀 方案一:直接调用 UI 组件(耦合强)
✅ 示例代码
javascript
import axios from 'axios'
import { message } from 'antd'
const request = axios.create()
request.interceptors.response.use(
res => {
if (res.data.code !== 0) {
message.error(res.data.message || '请求失败')
return Promise.reject(res.data)
}
return res.data
},
err => {
message.error(err.message || '网络错误')
return Promise.reject(err)
}
)
✅ 优点
- 上手快,适合小项目
- 没有额外封装成本
❌ 缺点
- UI 库(如 antd、element)直接耦合进 request 层
- 未来更换 UI 库代价大
✨ 方案二:全局 UI Handler 注入式调用(中等耦合)
✅ 核心思路
将 UI 提示通过 setUIHandler()
注入,request.ts
内只调用 handler,解耦 UI。
✅ 实现步骤
1. 定义 UI Handler 工具
javascript
// utils/globalUI.ts
export type UIHandler = {
error: (msg: string) => void
success?: (msg: string) => void
}
let handler: UIHandler | null = null
export function setUIHandler(h: UIHandler) {
handler = h
}
export function getUIHandler(): UIHandler {
if (!handler) throw new Error('UIHandler 未初始化')
return handler
}
2. 在 request.ts
中使用
javascript
import axios from 'axios'
import { getUIHandler } from './globalUI'
const request = axios.create()
request.interceptors.response.use(
res => {
if (res.data.code !== 0) {
getUIHandler().error(res.data.message || '请求失败')
return Promise.reject(res.data)
}
return res.data
},
err => {
getUIHandler().error(err.message || '网络错误')
return Promise.reject(err)
}
)
3. 在 App 入口注入 UI 实现
javascript
// App.tsx
import { message } from 'antd'
import { setUIHandler } from './utils/globalUI'
setUIHandler({
error: msg => message.error(msg),
})
✅ 优点
- UI 解耦
- 支持切换提示方式、mock、测试
- 适合中型项目
❌ 缺点
- 需要额外初始化步骤
- 无法处理组件内自定义提示(需手动处理)
📦 方案三:基于 EventBus 的解耦提示(推荐用于复杂场景)
✅ 核心思路
- 使用
mitt
、eventemitter3
或自定义 EventBus - 请求失败时发送事件,由 UI 组件统一监听提示
✅ 示例实现
1. 创建事件总线
javascript
// utils/eventBus.ts
import mitt from 'mitt'
export const eventBus = mitt<{
showError: string
}>()
2. 请求中发出错误事件
javascript
// request.ts
import axios from 'axios'
import { eventBus } from './eventBus'
const request = axios.create()
request.interceptors.response.use(
res => {
if (res.data.code !== 0) {
eventBus.emit('showError', res.data.message || '请求失败')
return Promise.reject(res.data)
}
return res.data
},
err => {
eventBus.emit('showError', err.message || '网络错误')
return Promise.reject(err)
}
)
3. UI 层监听事件统一提示
javascript
// GlobalErrorListener.tsx
import { useEffect } from 'react'
import { message } from 'antd'
import { eventBus } from './utils/eventBus'
export default function GlobalErrorListener() {
useEffect(() => {
const handler = (msg: string) => message.error(msg)
eventBus.on('showError', handler)
return () => eventBus.off('showError', handler)
}, [])
return null
}
javascript
// App.tsx
<GlobalErrorListener />
✅ 优点
- 完全解耦,适合复杂逻辑(如:错误码分类型处理)
- 支持全局监听和组件级复用
- UI 层控制力强
❌ 缺点
- 逻辑稍复杂,需要维护事件名和事件生命周期
- 非初学者首选
🧭 最终建议
- 👉 小项目 / 快速原型:方案一足够
- 👉 中型项目 / 渐进式解耦:方案二更优雅
- 👉 大型项目 / 多人协作 / 错误分类复杂:方案三最灵活,最可控
📌 写在最后
请求错误提示并不只是简单的 toast 问题,它关系到 UI 与数据层的架构解耦程度。选对合适的方案,能让你的项目后续更稳定、好维护。
如果你觉得这篇文章对你有帮助,欢迎 👍 点赞 + 收藏,如果你有更好的实践,也欢迎评论区一起交流~