Hello again! Did you ever try to display data from a remote backend in your React app? In the third part of this article series, you will learn how to fetch data from a REST API and use it inside your app.
Note: I recently published a new post about fetching data from a REST API with React. Have a look 😉
In the previous parts of this series you learned how to bootstrap a new React app with create-react-app and create a list component. Today we are going to connect this app to an existing REST API and use the fetched data to display our previously created list of contacts.
Over the whole series of articles, we’re going to build a functional contact list with React:
Part 1 – How to Create a React App with create-react-app
Part 2 – How to Create a React List Component
Part 3 – How to Connect your React App to a REST API
Article Roadmap
In this article, you will learn
- How to fetch data from a remote REST API
- How to store fetched data in the State of a React component
We will adapt our App component from part 2. First, it will retrieve contact data from a remote API and stores it in it’s State. Then we will pass the data inside State to our ContactList components props. This way, the app fetches contacts at the startup and fills our contact list with data.
Preparations
If you don’t have the source code of the previous part ready, you can clone it from GitHub, install the dependencies and start the app
git clone https://github.com/areiterer/contacts-manager.git cd contacts-manager git checkout part-3 npm install npm start
The app is now available at http://localhost:3000 in your browser and you should see where we’ve left off last time.
Fetching Data from a REST API
To fetch data from a REST API, you perform an AJAX request to a REST API which will return the requested information. There are several ways to fulfill this task. You could either do this the vanilla way by creating a XHRequest, or use some helper libraries to make things easier for you:
Those three are just the examples that came to my mind. If you do some research, you will stumble upon a lot more helper tools for AJAX requests.
In this article we’re going to use axios for our app. I’m not going to explain each of the listed tools, but feel free to use one of your own preference, if you want to. With React, you can use whatever library you want to fetch data from whichever backend. You’re not locked in to a certain toolset.
The following example shows, how you can perform an AJAX GET request with axios:
axios
.get("https://jsonplaceholder.typicode.com/users")
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
axios.get returns a Promise. That’s great, because you can chain functions to a Promise with .then. This means, everything you put into a .then that you chain to axios’ get function, will be executed after the data returned. We’ll need that later. Also, you can catch errors by chaining .catch to the call.
Handling State
Usually you want to store the data you fetched, for further usage. In React, you would do that in the Store of a component, so the app can react to the retrieved data.
We will choose the App component to do so, because it’s the root component of our ContactList component. So at startup, the App component will fetch the contact data from a server and stores it in it’s State. Because we will bind the props of ContactList to the State of App, the newly retrieved data will be passed, as soon as it is placed in the State.
I will describe how we store our stuff in the component’s State in the next section, so let’s get our hands dirty and work on our app!
Fetching Data into a React Component
Before we start, let’s talk about what we’re going to do and why.
Data is usually retrieved in a Lifecycle Method componentDidMount. At this point in the lifecycle of a React component, the component was already rendered and put into the DOM. Also, we have access to this.state which we need to store the received contact data.
This means, we’re going to perform our AJAX request to the server inside our componentDidMount lifecycle method. And as soon as we get the requested data back we set our State, which again triggers a re-render of our components because now they’ve got some data to show.
Also, I want to talk a bit about the REST API — the backend we’re going to use.
The Backend
For this app, we’re going to use an existing REST API to fetch our contact names: https://jsonplaceholder.typicode.com/users
If you visit the URL, you will see that it returns an array of users. For now, we’re taking the id and the name information to display our contact list.
But that’s enough talking for now, let’s finally begin!
Applying our Changes
As stated at the top of this article, we’re going to use axios to perform AJAX requests. To begin, let’s install axios: In your root directory (where your package.json is) execute the following command line:
npm i -S axios
Next, open your App.js and perform the following actions:
- add the componentDidMount lifecycle method to the App component.
- import axios from the just installed package
- add the axios GET request to componentDidMount to retrieve the contact data and store it in the App component’s State.
- bind the “contacts” prop of ComponentList to App component’s State to pass the contact data.
Here’s what your App.js should look like:
// ...
import axios from "axios";
class App extends Component {
// default State object
state = {
contacts: []
};
componentDidMount() {
axios
.get("https://jsonplaceholder.typicode.com/users")
.then(response => {
// create an array of contacts only with relevant data
const newContacts = response.data.map(c => {
return {
id: c.id,
name: c.name
};
});
// create a new "State" object without mutating
// the original State object.
const newState = Object.assign({}, this.state, {
contacts: newContacts
});
// store the new state object in the component's state
this.setState(newState);
})
.catch(error => console.log(error));
}
render() {
return (
<div className="App">
...
<ContactList contacts={this.state.contacts} />
</div>
);
}
}
export default App;
So what did we do? First, we added the componentDidMount lifecycle method to the App component.
Fetching the Data from a Rest API
We imported axios and performed an AJAX GET request to the REST API with axios.get(). Then we chain our “callback” with .then to the request, to execute our logic as soon as the data returns from the backend.
Use .map() to Pick Relevant Data
Like I told you before, we’re getting back way too much data for our use case, so we’re not going to store everything of that. With the help of map, we pick only the relevant data and put it into a new array of objects that only contain id and name of the returned contacts.
Use Object.assign to Merge new Data into the State Object
Then we create a new State object by using Object.assign(). That’s a way to make a copy of an object and alter it without mutating the original object. Note: NEVER modify the State object directly!
Let’s see what Object.assign does with our objects:
Object.assign({}, this.state, {contacts: newContacts});
The first argument: {} is the target object. All of the following objects are going to be merged together from right to left. So in our case, an object with a propery “contacts” (an array with our newContacts) will be merged into the current State object. Since there is already a “contacts” property, it will be overwritten by the content of the “contacts” property of the object next object. After that, the newly merged object is merged to the target object. Since it is empty, it is the initial State object with a replaced “contacts” property.
Finally – Set the new State
Now that we got our data, picked the relevant parts out of it and created a “new” State object, we store it in the State of the App component.
this.setState(newState)
This call, puts the “newState” object as our new State.
Note: You can also pass a function to setState. This is very important to know, because setState is asynchronous. So whenever you set your State depending on the current State of the application, pass a function to setState. Learn more about this in the React docs.
Passing the State Data to the ContactList
Instead of our static array of contact objects, we’re now able to bind our State to the ContactList element.
<ContactList contacts={this.state.contacts} />
By referencing this.state.contacts we make sure, that everytime the State changes, ContactList will get the new contacts array, which causes the contact list to be re-rendered.
Congratulations! You just connected your app to a remote REST API, fetched some data and stored it into the State.
Additional Exercise
Now that we retrieved contact data from a remote REST API, stored some data in our State and display it in a list of components by passing it down via props.
Your homework today will be to add some more fields to our Contact component. The REST backend we’re using returns a lot more data, so try to display also the email and the phone number of each contact in the list.
Wrapping Up
Today you’ve learned, that you can communicate with REST APIs by using tools like fetch, axios or superagent. Also, you learned that if you want to fetch data from a server at the startup of the app, you’ll do it in componentDidMount in a suitable component.
You also learned, how to set State and that you can pass an object or a function to setState.
Last but not least, you’ve learned that if you bind the State of a component to a prop of another component, it will automatically get the new data passed, every time the State changes.
Nice exercise, but I cannot make the “homework”, work. It does not look like any data other then name is being retrieved from the jsonplaceholder site. Not even id, which was in your original map statement and contactlist function. No matter what I do, only c.name resolves. Any hints?
Hello Mike,
thanks for reaching out! Can I see your code somewhere?
I just had a look at the example but everything seems about right:
If you type the API URL into your browser, you’ll see what you will get as a response: https://jsonplaceholder.typicode.com/users
Axios however, wraps the response into an array called
data
. This will contain all the returned user objects.You can have a look at the response by logging it to your browser console:
So if you map through
response.data
, you should be able to accessc.id
,c.name
, as well asc.username
,c.email
, etc.Let me know if this helped – I’d be happy to help you out 🙂
Cheers,
Andreas
I simply added
email: c.email, phone: c.phone
but this does not cause the contact’s email and phone to be displayed. Is there something else I am missing besides just adding these to the return block?
const newContacts = response.data.map(c => {
return {
id: c.id,
name: c.name,
email: c.email,
phone: c.phone,
};
});
//nothing else is changed
Oh, now I understand.
What you did here, is to add more fields to the objects in your ‘s render method, as well as the component.
newContacts
array. But yourContactList
component still only displays the name of the contact.That’s why you would have to change the
First of all, you have to make sure your component accepts (and renders) either more props for email and phone, so you can pass it like
<Contact name="c.name" email="c.email" ... />
, or you refactor it to take a whole contact object like<Contact contact="c" />
, whichever suits you the best.Then, change your so you actually pass the values to
I hope I could help understand the issue a bit better. Let me know if it worked!
Got it! Thanks!
What about caching? How one solves that if I navigate away and then back? If the REST api response is still valid, one doesn’t want to fetch it again.
Hello! You’re right, as this is a basic example, I didn’t consider caching the response.
There are more possibilities to achieve reusing the fetched data. If you only want to keep it when the user navigates away but stays inside the app, you can store the data in the state of a parent component that doesn’t get unmounted (or in a global state, e.g. when using redux)
Otherwise you could store the data in localstorage and load this data before/instead performing a new request – but then you have to think about how you intend to invalidate the cache.
I hope I could help,
Cheers,
Andreas
Hello,
Would you consider do an part 4 on redux 🙂
Thanks
Hi!
I definitely plan on writing about using redux too. Is there anything specific that you’re struggling with?