This content originally appeared on Envato Tuts+ Tutorials and was authored by Gigi Sayfan
React is arguably the most popular library for building interactive web applications. However, React is not a full-fledged web framework. It focuses on the view part of the venerable MVC model.
There is a whole React ecosystem that addresses other aspects. In this tutorial, you'll learn about one of the most basic elements of any web application—how to fetch data to display. This is not trivial. There are several places in the React component hierarchy where you can fetch data. When to fetch data is another concern. You also need to consider what technology to use for fetching your data and where to store it.
At the end of this tutorial, you'll have a clear picture of how data fetching works in React, the pros and cons of different approaches, and how to apply this knowledge to your React applications.
Getting Started
Let's create a skeleton for our React app with create-react-app:
1 |
create-react-app react-data-fetcher |
The result is a pretty elaborate directory structure. Read the excellent README file if you're unfamiliar with create-react-app.
Creating a Simple Server
I created a simple server for storing and serving quotes. It is not the focus of this tutorial, and its role is to provide a remote API for demonstrating how to fetch data with React. Just to satisfy your curiosity, it is a Python 3 application based on the hug framework and uses Redis as persistent storage.
The API is extremely simple. There is a single endpoint, /quotes
. It returns all the stored quotes in response to an HTTP GET request, and you can add new quotes by sending an HTTP POST request.
The full source code is available on GitHub.
Demo App Overview
The demo app is a React application that communicates with the quote service, displays all the quotes, and lets you add new quotes.
The app structure is very simple. I started with a skeleton created by create-react-app and added two components in the src sub-directory: QuoteList and AddQuoteForm. Here is the directory structure (excluding node_modules):
1 |
~/git/react-data-fetcher > tree -I node_modules -L 2 |
2 |
. |
3 |
├── README.md |
4 |
├── README2.md |
5 |
├── package.json |
6 |
├── public |
7 |
│ ├── favicon.ico |
8 |
│ ├── index.html |
9 |
│ └── manifest.json |
10 |
├── src |
11 |
│ ├── AddQuoteForm.css |
12 |
│ ├── AddQuoteForm.js |
13 |
│ ├── App.css |
14 |
│ ├── App.js |
15 |
│ ├── App.test.js |
16 |
│ ├── QuoteList.js |
17 |
│ ├── index.css |
18 |
│ ├── index.js |
19 |
│ └── registerServiceWorker.js |
20 |
└── yarn.lock |
21 |
2 directories, 16 files |
The full source code is available on GitLab.
Displaying Quotes
The QuoteList
functional component displays a list of quotes as a bullet list. It expects an array of strings:
1 |
import React from 'react' |
2 |
const QuoteList = ({quotes}) => |
3 |
quotes.map(quote => <li key={quote}>{quote}</li>) |
4 |
export default QuoteList |
It is a child component of the main App
component.
Fetching Data With the Fetch API
The Fetch API is a promise-based API that returns a response object. In order to get to the actual JSON content, you need to invoke the json()
method of the response object.
1 |
fetchQuotes = () => { |
2 |
this.setState({...this.state, isFetching: true}) |
3 |
fetch(QUOTE_SERVICE_URL) |
4 |
.then(response => response.json()) |
5 |
.then(result => this.setState({quotes: result, |
6 |
isFetching: false})) |
7 |
.catch(e => console.log(e)); |
8 |
}
|
9 |
}
|
Placing Your Data-Fetching Code
React is, of course, all about components. The question of where to place data-fetching code is important. If you factor your code well, you'll have a lot of generic components and some application-specific components. React and JavaScript in general are very flexible, so it is possible to inject logic anywhere.
Fetching quotes from a REST API requires some form of polling, since I want the quotes to be always up to date. But the initial fetch is also important. React components have lifecycle methods where you can implement logic that will execute at a particular time. The componentDidMount()
method fires when the component can be accessed and its state modified. It is the perfect spot to initiate data fetching.
Here is what it looks like:
1 |
componentDidMount() { |
2 |
this.fetchQuotes() |
3 |
}
|
If you really want to cut down on the time to first view, you may consider using the componentWillMount()
to initiate your async fetching, but you risk having the fetch complete before the component is mounted. I don't recommend this approach.
Check out Mastering the React Lifecycle Methods for further details.
Choosing How Often to Fetch Data
The initial fetch in componentDidMount()
is great, but I want to update the quotes frequently. In a REST-based API, the only solution is to periodically poll the server. The quote service is very basic and always returns all the quotes.
More scalable services will provide a way to check for updates or even using HTTP if-modify-since or eTag. Our demo application just fetches everything every five seconds by starting a timer in componentDidMount()
and cleaning up in componentWillUnmount()
:
1 |
componentDidMount() { |
2 |
this.fetchQuotes() |
3 |
this.timer = setInterval(() => this.fetchQuotes(), 5000); |
4 |
}
|
5 |
|
6 |
componentWillUnmount() { |
7 |
this.timer = null; |
8 |
}
|
The polling duration is an app-specific decision. If you need real-time updates and/or polling is stressing the back end too much, consider using WebSockets instead of REST.
Dealing With Long-Running Data Fetching
Sometimes data fetching can take a long time. In that case, displaying a progress bar or a shiny animation to let the user know what's going on can contribute a lot to the user experience. This is especially important when the user initiates the data fetching (eg. by clicking a search button).
In the demo app, I simply display a message saying "Fetching quotes..." while a fetch is ongoing. In the render()
method of the main App
component, I utilize conditional rendering by checking the state.isFetching
member.
1 |
render() { |
2 |
const title = 'Quotes for ya!' |
3 |
let now = new Date() |
4 |
return ( |
5 |
<div className='App'> |
6 |
<h2 className='App-title'>{title}</h2> |
7 |
<p>{this.state.isFetching ? 'Fetching quotes...' : ''}</p> |
8 |
<QuoteList quotes={this.state.quotes} /> |
9 |
<AddQuoteForm quote_service_url={QUOTE_SERVICE_URL}/> |
10 |
</div> |
11 |
);
|
12 |
}
|
The fetchQuotes()
method takes care of updating state.isFetching
by initializing it to true when it starts and setting it back to false when receiving the quotes:
1 |
fetchQuotes = () => { |
2 |
this.setState({...this.state, isFetching: true}) |
3 |
fetch(QUOTE_SERVICE_URL) |
4 |
.then(response => response.json()) |
5 |
.then(result => this.setState({quotes: result, |
6 |
isFetching: false})) |
7 |
.catch(e => console.log(e)); |
8 |
}
|
9 |
}
|
Handling Errors
I do the very minimum of error handling here by logging caught errors to the console. Depending on your application, you may invoke some retry logic, notify the user, or display some fallback content.
Using Axios
An alternative to the Fetch API is Axios. Many developers choose to replace Fetch with Axios because of the extra power and convenience of the Axios library.
Here is what the fetchQuotes
function would look like with Axios:
1 |
fetchQuotes = () => { |
2 |
this.setState({...this.state, isFetching: true}) |
3 |
axios.get(QUOTE_SERVICE_URL) |
4 |
.then(response => this.setState({quotes: response.data, |
5 |
isFetching: false})) |
6 |
.catch(e => console.log(e); |
7 |
}
|
This is very similar, but a little more concise. The submit logic can be as simple as seen below.
1 |
handleSubmit = event => { |
2 |
axios.post(this.props.quote_service_url, |
3 |
{'quote': this.state.quote}) |
4 |
.then(r => console.log(r)) |
5 |
.catch(e => console.log(e)); |
6 |
event.preventDefault(); |
7 |
}
|
With this basic introduction to Axios, let's learn more about the differences between the two libraries.
Differences between Axios and Fetch
JSON Data Transformation
If you look at Axios, the syntax appears as below. Axios takes care of converting the data to JSON
.
1 |
const url = '' |
2 |
const data = {}; |
3 |
axios
|
4 |
.post(url, data, { |
5 |
headers: { |
6 |
Accept: "application/json", |
7 |
"Content-Type": "application/json;charset=UTF-8", |
8 |
},
|
9 |
})
|
10 |
.then(({data}) => { |
11 |
console.log(data); |
12 |
});
|
On the other hand, the basic syntax of fetch
is as below. With fetch
, you need to convert the data to a string
, using JSON.stringify
.
1 |
const url = ""; |
2 |
const options = { |
3 |
method: "POST", |
4 |
headers: { |
5 |
Accept: "application/json", |
6 |
"Content-Type": "application/json;charset=UTF-8", |
7 |
}, |
8 |
body: JSON.stringify({}), |
9 |
}; |
10 |
fetch(url, options) |
11 |
.then((response) => response.json()) |
12 |
.then((data) => { |
13 |
console.log(data); |
14 |
}); |
Also, the response must be converted back to JSON with fetch
.
Response Timeout With Axios
In the example above, we had to implement logic whereby the API endpoint gets called after a predefined timeout duration. This is a major reason why fetch
is avoided in real time applications. Axios makes response timeout extremely simple. This is an optional property in the config object of Axios.
1 |
axios({ |
2 |
method: 'post', |
3 |
url: '', |
4 |
timeout: 5000, |
5 |
data: {} |
6 |
})
|
7 |
.then(response => {/* handle the response */}) |
8 |
.catch(error => console.error('timeout exceeded')) |
Interceptors
One of the main reasons why developers prefer Axios over fetch
is access to HTTP interceptors. By definition, HTTP interceptors are useful when you want to change, or inspect a HTTP request. The code does not have to be separated for every request, when you use interceptors. HTTP interceptors give a global strategy for handling requests, and responses.
So, how do we achieve HTTP interception with Axios? Here is a snippet to help you.
1 |
axios.interceptors.request.use(config => { |
2 |
console.log('Hello There!'); |
3 |
return config; |
4 |
});
|
5 |
|
6 |
// sent a GET request
|
7 |
axios.get('') |
8 |
.then(response => { |
9 |
console.log(response.data); |
10 |
});
|
In the above piece of code, axios.interceptors.request.use
method is used to tell the application what has to be done before firing the request. This would be a great place to inject authentication information into the request.
On the other hand, code can become very complicated when you want to intercept a fetch
request. You have to override the global implementation of fetch
to define interceptors. And this is not an easy, or a clean task.
Simultaneous Requests
In a real time app, you will need a solution that can send multiple requests simultaneously. This is where Axios proves to be useful. The Axios library comes with many methods like all()
and spread()
to handle multiple requests. Here is a simple example, to help you understand how multiple requests can be sent in axios.
Here, the console.log
statements will print only after both the API requests have completed successfully.
1 |
axios.all([ |
2 |
axios.get('https://'), |
3 |
axios.get('https://') |
4 |
])
|
5 |
.then(axios.spread((a, b) => { |
6 |
console.log(a); |
7 |
console.log(b); |
8 |
}));
|
To achieve the above logic in fetch
, you have to make use of Promise.all
. Let us translate the above logic into a fetch
request.
1 |
Promise.all([ |
2 |
fetch('https://'), |
3 |
fetch('https://') |
4 |
])
|
5 |
.then(async([res1, res2]) => { |
6 |
const a = await res1.json(); |
7 |
const b = await res2.json(); |
8 |
console.log(a'); |
9 |
console.log(b'); |
10 |
})
|
11 |
.catch(error => { |
12 |
console.log(error); |
13 |
});
|
Conclusion
In this post, you have seen many interesting facts, and ways of implementing of API requests in a React Application.
You have learnt how to fetch data asynchronously in a React application. And, we also discussed relevant lifecycle methods, polling, progress reporting, and error handling. We also compared the two main promise-based libraries: the Fetch API and Axios.
This post was updated with contributions from Divya Dev.
This content originally appeared on Envato Tuts+ Tutorials and was authored by Gigi Sayfan
Gigi Sayfan | Sciencx (2018-02-26T19:31:11+00:00) Fetching Data in Your React Application. Retrieved from https://www.scien.cx/2018/02/26/fetching-data-in-your-react-application/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.