How to fetch data in React (with error handling)
Sometimes people get entangled in the component lifecycle of React to get data from API, I mean which tool/library would be best to use and what is the right method in React component’s lifecycle to fetch it correctly.
There are many popular libraries and built-in API for fetching data in the React component. In this tutorial, I will describe some of the best that I have found on the web.
In this tutorial, I'm using Github's users API to pull data.
Table of Contents
How to fetch data in React using native window.fetch API
import React, { Component } from 'react';
class App extends Component {
constructor(props) {
super(props);
// Initialize state first
this.state = {
users: [],
err: null,
isLoading: false
}
}
}
Then initialize the React lifecycle method componentDidMount to call fetch request.
componentDidMount() {
this.setState({ isLoading: true })
let api_url = 'https://api.github.com/users';
// Now, use JavaScript's native Fetch API to get
// users list from Github API
fetch(api_url)
.then(res => {
// Unfortunately, fetch doesn't send (404 error) into the cache itself
// You have to send it, as I have done below
if(res.status >= 400) {
throw new Error("Server responds with error!");
}
return res.json();
})
.then(users => {
this.setState({
users,
isLoading: false
})
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components
err => {
this.setState({
err,
isLoading: false
})
});
}
Now, this.state.users
has users data array, render it into DOM.
render() {
// Extract states in the local variable
let {users, err, isLoading} = this.state;
if(err) {
// Here you can either render error message or Error component
// In this example, I have used message
return (
<div> { err.message } </div>
)
}
if(isLoading) {
return (
<div> Loading... </div>
)
}
return (
<div>
{/* Here you can check whether the users array contains data or not. */}
{ users.length > 0 ?
<ul>
{users.map( user => (
<li key={user.id} >
{ user.login }
</li>
))}
</ul>
: <div> No user found! </div> }
</div>
)
}
If you want to use the native fetch
API in your App, I recommend using cross-fetch as a polyfill because some old browser/JS engine doesn't fully support fetch.
How to fetch data in React using Axios HTTP client
Axios is another popular option which you can use to make HTTP requests in React Component. Axios is Promise-based and works for both client (Browser) and server (Node) so you don't need to worry about, whether you hit a request from the server or in your browser.
Axios is a third-party tool and you have to install it first from npm or yarn to use.
import React, { Component } from 'react';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
// Initialize state first
this.state = {
users: [],
err: null,
isLoading: false
}
}
}
Then initialize componentDidMount lifecycle method of React to trigger axios request
componentDidMount() {
this.setState({ isLoading: true })
let api_url = 'https://api.github.com/users';
// Now, To pull data from API use axios get method
axios.get(api_url)
// axios automatically changes the response to JSON
.then(users => {
this.setState({
users: users.data,
isLoading: false
})
})
.catch(err => {
this.setState({
err,
isLoading: false
})
});
}
You can create a new instance of axios with a custom config as I created below.
const request = axios.create({
baseURL: 'https://api.github.com/',
timeout: 1000,
headers: {
'Content-Type': 'application/json',
// or whatever you want, in key/value pair
}
})
// Now, you can use it like this -> request.get() or request.post()
request.get('users')
.then(res => this.setState({ users: res.data, isLoading: false }))
.catch(err => this.setState({ err, isLoading: false }));
How to fetch data in React using async/await
If you used JavaScript Promises
before in your code then it will more easy for you to understand async/await.
It's a special syntax to work with asynchronous requests in a more comfortable way. From async/await you can fetch data inside a React Component from API in a more precise way.
To use, you must include the async
keyword before the function
keyword. async
word before function means that a function will always return a promise and await
makes JavaScript wait until promise settled and return its results.
In the example below, I am going to use it along with axios to pull data.
async getUsers() {
let api_url = 'https://api.github.com/users';
try {
const res = await axios.get(api_url);
this.setState({
users: res.data,
isLoading: false
})
} catch(err) {
this.setState({
err,
isLoading: false
})
}
}
// and don't forget to bind getUsers function
// with component to access "this.setState" inside getUsers
How to Fetch data using React Hooks
In version 16.8.0
React added some new function called React Hooks. Hooks allow you to use state and other React features without writing a class
.
What is Hook?
Hooks are functions that let you "hook into" React state and lifecycle features from function components. Hooks don't work inside classes, they let you use React without classes.
React provides some built-in Hooks like useState
, useEffect
, useContext
, or either you can build your own custom hooks with "use" prefix.
In the example below, I am using useState
and useEffect
hook to pull data and render it into DOM.
import React, { useState, useEffect } from 'react';
let api_url = 'https://api.github.com/users';
// App component
function App() {
// Initialize state first
let [users, setUsers] = useState([]);
let [isLoaded, setIsLoaded] = useState(false);
let [err, setErr] = useState(null);
useEffect(() => {
const getUsers = () => {
fetch(api_url)
.then(res => {
// Unfortunately, fetch doesn't send (404 error)
// into the cache itself
// You have to send it, as I have done below
if (res.status >= 400) {
throw new Error("Server responds with error!")
}
return res.json()
})
.then(users => {
setUsers(users)
setIsLoaded(true)
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components
err => {
setErr(err)
setIsLoaded(true)
})
};
getUsers()
}, [])
Then render users data array into DOM using return
if (err) {
return <div> {err.message} </div>
} else if (!isLoaded) {
return <div> Loading... </div>
} else {
return (
<ul>
{
users.map(user => (
<li key={user.id}>
{user.login}
</li>
))
}
</ul>
)
}
Conclusion
Above, I have covered some popular tools and tactics to fetching data in React. You can either use native class
component or modern React hooks (added in version 16.8.0), it's up to you.