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

如何实现通用错误信息展示函数/组件?解决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:

  1. 新建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()
  1. 修改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)
  }
)
  1. 在根组件中监听事件:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 06:45:06