如何实现通用错误信息展示函数/组件?解决React Hook调用限制
问题描述
我想定义一个函数,在后端返回错误时,通过Chakra-UI的toast组件在前端展示响应里的错误原因。用Axios创建了请求实例并设置响应拦截器,但因为React规定Hook(useToast/toast)不能在顶层调用,导致编译报错。以下是我的代码:
const request = axios.create({ baseURL: [], timeout: 5000 }) export const error= (msg: string) => { toast({ position: 'top', status: 'error', duration: 2000, description: msg }) } request.interceptors.response.use( response => { if (response.status !== 'ok') { error(response.reason) } return response }, error => { console.log('err' + error) // for debug return Promise.reject(error) } )
解决方案
方法一:使用Chakra-UI的createStandaloneToast(推荐)
Chakra-UI提供了createStandaloneToast方法,允许在React组件树之外调用toast,完美适配axios拦截器这种非组件环境的场景。
修改代码如下:
import { createStandaloneToast } from '@chakra-ui/react' // 创建独立的toast实例 const { toast } = createStandaloneToast() const request = axios.create({ baseURL: '', // 替换为实际后端接口地址 timeout: 5000 }) // 错误提示函数,现在可在顶层调用 const showErrorToast = (msg: string) => { toast({ position: 'top', status: 'error', duration: 2000, description: msg }) } request.interceptors.response.use( response => { // 注意:若后端自定义status字段,需确认路径(例:response.data.status) if (response.data?.status !== 'ok') { showErrorToast(response.data?.reason || '请求失败') } return response }, error => { // 处理HTTP层面错误(如网络异常、404/500等) const errorMsg = error.response?.data?.reason || error.message || '请求出错' showErrorToast(errorMsg) console.log('err' + error) // for debug return Promise.reject(error) } )
注意:确保已安装@chakra-ui/react及相关依赖(@emotion/react、@emotion/styled、framer-motion)。
方法二:在组件内封装请求并处理错误
将请求逻辑封装到React自定义Hook中,在Hook内部调用useToast,符合React Hook调用规则:
import { useToast } from '@chakra-ui/react' import axios from 'axios' const request = axios.create({ baseURL: '', timeout: 5000 }) export const useApiRequest = () => { const toast = useToast() const handleResponseError = (response) => { if (response.data?.status !== 'ok') { toast({ position: 'top', status: 'error', duration: 2000, description: response.data?.reason || '请求失败' }) } return response } const handleRequestError = (error) => { const errorMsg = error.response?.data?.reason || error.message || '请求出错' toast({ position: 'top', status: 'error', duration: 2000, description: errorMsg }) console.log('err' + error) return Promise.reject(error) } // 动态添加拦截器,避免重复注册 const addInterceptors = () => { request.interceptors.response.use(handleResponseError, handleRequestError) } return { request, addInterceptors } }
组件内使用示例:
import { useEffect } from 'react' import { useApiRequest } from './api' const MyComponent = () => { const { request, addInterceptors } = useApiRequest() useEffect(() => { addInterceptors() }, []) const fetchData = async () => { try { const res = await request.get('/some-api') // 处理成功响应数据 } catch (err) { // 额外错误处理逻辑 } } return <button onClick={fetchData}>获取数据</button> }
方法三:使用事件总线传递错误消息
创建全局事件发射器,在axios拦截器中触发错误事件,在React组件中监听事件并调用toast:
- 新建
eventBus.js文件:
class EventBus { constructor() { this.events = {} } on(event, callback) { if (!this.events[event]) this.events[event] = [] this.events[event].push(callback) } emit(event, data) { this.events[event]?.forEach(callback => callback(data)) } off(event, callback) { if (!this.events[event]) return this.events[event] = this.events[event].filter(cb => cb !== callback) } } export default new EventBus()
- 修改axios拦截器:
import eventBus from './eventBus' const request = axios.create({ baseURL: '', timeout: 5000 }) request.interceptors.response.use( response => { if (response.data?.status !== 'ok') { eventBus.emit('showErrorToast', response.data?.reason || '请求失败') } return response }, error => { const errorMsg = error.response?.data?.reason || error.message || '请求出错' eventBus.emit('showErrorToast', errorMsg) console.log('err' + error) return Promise.reject(error) } )
- 在根组件中监听事件:
import { useEffect } from 'react' import { useToast } from '@chakra-ui/react' import eventBus from './eventBus' const App = () => { const toast = useToast() useEffect(() => { const handleErrorToast = (msg) => { toast({ position: 'top', status: 'error', duration: 2000, description: msg }) } eventBus.on('showErrorToast', handleErrorToast) // 组件卸载时清理监听 return () => { eventBus.off('showErrorToast', handleErrorToast) } }, [toast]) return <div>应用内容</div> }
内容的提问来源于stack exchange,提问作者zephyrus
相关产品推荐
相关产品推荐

