Reputation: 7735
I have a React component that calls a function getAllPeople
:
componentDidMount() {
getAllPeople().then(response => {
this.setState(() => ({ people: response.data }));
});
}
getAllPeople
is in my api
module:
export function getAllPeople() {
return axios
.get("/api/getAllPeople")
.then(response => {
return response.data;
})
.catch(error => {
return error;
});
}
I think this is a very basic question, but assuming I want to handle the error in my root component (in my componentDidMount
method), not in the api
function, how does this root component know whether or not I the axios
call returns an error? I.e. what is the best way to handle errors coming from an axios
promise?
Upvotes: 46
Views: 126999
Reputation: 5095
There are 2 options to handle error with axios in reactjs
Using catch method:
axios.get(apiUrl).then(()=>{
//respons
}).catch((error)=>{
//handle error here
})
try catch
try {
const res = await axios.get(apiUrl);
} catch(err){
//handle error here...
}
Upvotes: 1
Reputation: 1433
I just combined both answers by Yevhenii Herasymchuk and GeorgeCodeHub, fixed some mistakes and ported it into React hooks. So here is my final working version:
// [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;
Usage:
// Wrap it around your Layout or any component that you want
return (
<AxiosErrorHandler>
<div>Hello from my layout</div>
</AxiosErrorHandler>
);
Upvotes: 2
Reputation: 5243
The getAllPeople
function already returns the data or error message from your axios
call. So, in componentDidMount
, you need to check the return value of your call to getAllPeople
to decide whether it was an error or valid data that was returned.
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
}
});
}
If you want to return a promise from your api, you should not resolve the promise returned by your axios
call in the api. Instead you can do the following:
export function getAllPeople() {
return axios.get("/api/getAllPeople");
}
Then you can resolve in componentDidMount
.
componentDidMount() {
getAllPeople()
.then(response => {
this.setState(() => ({ people: response.data}));
})
.catch(error => {
// your error handling goes here
}
}
Upvotes: 22
Reputation: 301
The solution from Yevhenii Herasymchuk was very close to what I needed however, I aimed for an implementation with functional components so that I could use Hooks and Redux.
First I created a wrapper:
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;
Then I wrapped the part of the project that I needed to check how axios behaved.
<ErrorHandler>
<MainPage/>
</ErrorHandler>
Lastly, I import the axios instance(http) wherever I need it in the project.
Hope it helps anyone that wishes for a different approach.
Upvotes: 0
Reputation: 4957
Better way to handle API error with Promise catch method*.
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);
});
Upvotes: 74
Reputation: 2137
My suggestion is to use a cutting-edge feature of React. Error Boundaries
This is an example of using this feature by Dan Abramov. In this case, you can wrap your component with this Error Boundary component. What is special for catching the error in axios is that you can use interceptors for catching API errors. Your Error Boundary component might look like
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;
Then, you can wrap your root component passing axios instance with it
errorHandler(Checkout, api)
As a result, you don't need to think about error inside your component at all.
Upvotes: 17
Reputation: 1726
You could check the response before setting it to state
. Something like
componentDidMount() {
getAllPeople().then(response => {
// check if its actual response or error
if(error) this.setState(() => ({ error: response }));
else this.setState(() => ({ people: response}));
});
}
Its relying on the fact that axios
will return different objects for success and failures.
Upvotes: 0