在React中处理Axios错误

43

我有一个React组件,调用了一个名为getAllPeople的函数:

componentDidMount() {
   getAllPeople().then(response => {
      this.setState(() => ({ people: response.data }));
    });
  } 

getAllPeople在我的api模块中:

export function getAllPeople() {
  return axios
    .get("/api/getAllPeople")
    .then(response => {
      return response.data;
    })
    .catch(error => {
      return error;
    });
}

我认为这是一个非常基本的问题,但是假设我想在我的根组件(在我的componentDidMount方法中)处理错误,而不是在api函数中处理错误,那么这个根组件如何知道是否axios调用返回错误?也就是说,处理从axios promise返回的错误的最佳方式是什么?

7个回答

70

使用Promise catch方法更好地处理API错误*的方式。

axios.get(people)
    .then((response) => {
        // Success
    })
    .catch((error) => {
        // Error
        if (error.response) {
            // The request was made and the server responded with a status code
            // that falls out of the range of 2xx
            // console.log(error.response.data);
            // console.log(error.response.status);
            // console.log(error.response.headers);
        } else if (error.request) {
            // The request was made but no response was received
            // `error.request` is an instance of XMLHttpRequest in the 
            // browser and an instance of
            // http.ClientRequest in node.js
            console.log(error.request);
        } else {
            // Something happened in setting up the request that triggered an Error
            console.log('Error', error.message);
        }
        console.log(error.config);
    });

21

getAllPeople函数已经从axios调用中返回了数据或错误信息。因此,在componentDidMount中,您需要检查对getAllPeople的调用返回值,以确定是返回了错误还是有效数据。

componentDidMount() {
   getAllPeople().then(response => {
      if(response!=error) //error is the error object you can get from the axios call
         this.setState(() => ({ people: response}));
      else { // your error handling goes here
       }
    });
  } 

如果您想从API中返回一个Promise,那么您不应该在API中解析由axios调用返回的Promise,而是可以按照以下方式进行操作:

export function getAllPeople() {
  return axios.get("/api/getAllPeople");
}

然后您可以在 componentDidMount 中解决。

componentDidMount() {
   getAllPeople()
   .then(response => {          
         this.setState(() => ({ people: response.data}));
     })
   .catch(error => { 
         // your error handling goes here
     }
  } 

我难道不已经按照你的建议从我的 API 返回了 axios.get(...) 吗? - GluePear
我编辑了我的答案并添加了代码以澄清我的意思。 - palsrealm

17

我的建议是使用React的尖端功能。 Error Boundaries

这是Dan Abramov使用此功能的一个示例。 在这种情况下,您可以使用此错误边界组件包装您的组件。 对于捕获axios中的错误特别之处在于,您可以使用拦截器来捕获API错误。 您的错误边界组件可能会像这样:

import React, { Component } from 'react';

const errorHandler = (WrappedComponent, axios) => {
  return class EH extends Component {
    state = {
      error: null
    };

    componentDidMount() {
      // Set axios interceptors
      this.requestInterceptor = axios.interceptors.request.use(req => {
        this.setState({ error: null });
        return req;
      });

      this.responseInterceptor = axios.interceptors.response.use(
        res => res,
        error => {
          alert('Error happened');
          this.setState({ error });
        }
      );
    }

    componentWillUnmount() {
      // Remove handlers, so Garbage Collector will get rid of if WrappedComponent will be removed 
      axios.interceptors.request.eject(this.requestInterceptor);
      axios.interceptors.response.eject(this.responseInterceptor);
    }

    render() {
      let renderSection = this.state.error ? <div>Error</div> : <WrappedComponent {...this.props} />
      return renderSection;
    }
  };
};

export default errorHandler;

接下来,您可以使用axios实例包装您的根组件

errorHandler(Checkout, api)

因此,您根本不需要考虑组件内部的错误。


这似乎是一个很好的例子,但我不明白 WrappedComponent 是什么?另外,应该把 errorHandler(Checkout, api) 放在哪里? - Francisco Souza
我的响应拦截器没有检测到500错误。 - uzaif
嗨@yevhenii,我只是好奇API是否在Checkout的componentDidMount中。如果API没有暴露给它,那么错误处理程序如何捕获它呢?有什么策略吗? - TOS
@TOS 你说的API是什么意思?Axios负责所有调用,并且针对每个调用都有拦截器。在componentDidMount中看到的函数不是API调用,更像是为每个调用设置中间件。 是的,你必须传递axios的实例。 - Yevhenii Herasymchuk

1

在Reactjs中处理axios错误有两个选项:

  1. 使用catch方法:

    axios.get(apiUrl).then(()=>{ //响应处理 }).catch((error)=>{ //处理错误 })

  2. try catch

    try { const res = await axios.get(apiUrl); } catch(err){ //处理错误... }


1

我刚刚将Yevhenii HerasymchukGeorgeCodeHub的答案结合起来,修正了一些错误,并将其移植到React hooks中。这是我的最终工作版本:

// [project-directory]/src/lib/axios.js

import Axios from 'axios';

const axios = Axios.create({
  baseURL: process.env.NEXT_PUBLIC_BACKEND_URL,
  headers: {
    'X-Requested-With': 'XMLHttpRequest',
  },
  withCredentials: true,
});

export default axios;



// [project-directory]/src/components/AxiosErrorHandler.js

import {useEffect} from 'react';
import axios from '@/lib/axios';

const AxiosErrorHandler = ({children}) => {
  useEffect(() => {
    // Request interceptor
    const requestInterceptor = axios.interceptors.request.use((request) => {
      // Do something here with request if you need to
      return request;
    });

    // Response interceptor
    const responseInterceptor = axios.interceptors.response.use((response) => {
      // Handle response here

      return response;
    }, (error) => {
      // Handle errors here
      if (error.response?.status) {
        switch (error.response.status) {
          case 401:
            // Handle Unauthenticated here
            break;
          case 403:
            // Handle Unauthorized here
            break;
          // ... And so on
        }
      }

      return error;
    });

    return () => {
      // Remove handlers here
      axios.interceptors.request.eject(requestInterceptor);
      axios.interceptors.response.eject(responseInterceptor);
    };
  }, []);

  return children;
};

export default AxiosErrorHandler;

使用方法:

// Wrap it around your Layout or any component that you want

return (
  <AxiosErrorHandler>
    <div>Hello from my layout</div>
  </AxiosErrorHandler>
);

你如何设置授权头?将请求拦截器提取到axios库中,只在错误处理程序中添加响应拦截器是否有意义?或者将其添加到axios.create({})中,算了,我只是在胡言乱语。 - wojg21

0

在将响应设置为state之前,您可以先检查一下它。就像这样:

componentDidMount() {
   getAllPeople().then(response => {
       // check if its actual response or error
        if(error) this.setState(() => ({ error: response }));
        else this.setState(() => ({ people: response}));
    });
  }

它依赖于这样一个事实,即axios会针对成功和失败返回不同的对象。


0

Yevhenii Herasymchuk的解决方案非常接近我所需的,但我希望使用功能组件来实现,以便我可以使用Hooks和Redux。

首先,我创建了一个包装器:

export const http = Axios.create({
    baseURL: "/api",
    timeout: 30000,
});

function ErrorHandler(props) {

useEffect(() => {
    //Request interceptor
    http.interceptors.request.use(function (request) {
        // Do something here with Hooks or something else
        return request;
    });

    //Response interceptor
    http.interceptors.response.use(function (response) {
        if (response.status === 400) {
            // Do something here with Hooks or something else
            return response;
        }
        return response;
    });
}, []);

return props.children;
}

export default ErrorHandler;

然后我包装了项目的一部分,以便检查axios的行为。

<ErrorHandler>
  <MainPage/>
</ErrorHandler>

最后,在项目中需要时,我会导入 axios 实例(http)。
希望这对任何希望采用不同方法的人有所帮助。

1
你没有在组件外注销处理程序。 - George Mavritsakis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接