How to make an API call in React App
This tutorial explains about how to consume web services in React application and populate the same in React component.
This is a continuation of my previous one which explains about how to kick start a new React project using ReactBoilerPlate.
What’s in this tutorial?
- Consume/Fetch/Call web services using Fetch API
- Populate response in React component
Web Services to consume
You can use any service (URL) to learn this. I am going to demonstrate it by consuming Accounts services (Written in SpringBoot).
Getting Started with SpringBoot: Scaffolding & Annotations (Part2)
Spring Boot Deep Dive — RestController (Part3)
Getting started with Spring Data JPA, Hibernate ORM & Repository (Part 4)
Make Web Services Ready to Consume in Local
SpringBoot project can be found in GitHub.
Checkout data-jpa-end branch
git clone https://github.com/ragunathrajasekaran/em-api.git
cd em-api
git checkout data-jpa-end
Run MySql
Run MySql local instance as mentioned in blog or follow the below steps.
docker-compose up
If you see the below issue then start the docker container using its id
docker ps -a
docker start 0ee6ddeba0ca
Run SpringBoot Project
gradle build
gradle bootrun
PostMan Collection Import
I have committed the postman collection in github as em-apicollection.postman_collection.json. Please import the same if you would prefer to use Postman.
Accounts web services are ready to consume in localhost . Let’s verify by hitting health endpoint.
Reset MySql (If you want to)
If you want to reset accounts table, use the following queries to delete all existing rows and insert the new one.
delete from accounts;
INSERT INTO accounts (id, description, title) VALUES (1, 'My Savings Account', 'Savings');
INSERT INTO accounts (id, description, title) VALUES (2, 'My Credit Account', 'Credit');
INSERT INTO accounts (id, description, title) VALUES (3, 'My Debit Account', 'Debit');
INSERT INTO accounts (id, description, title) VALUES (4, 'My Wish List', 'WishList');
Recap from previous posts
In Previous Post, we have developed the following components(HomeContainer, AccountList & AccountListItem)
Checkout code from Github to begin (data-fetch-begin branch)
git clone -b data-fetch-begin https://github.com/ragunathrajasekaran/em-ui.git
HomeContainer
- Top level container
- It has a local state
- It has a hard coded accounts data (We are going to refactor and fetch accounts through web services)
AccountList
- Receives accounts from HomeContainer as props
- Iterate accounts array and render AccountListItem for each Account
AccountListItem
- Receives an account information from AccountList as props
- Present it in UI using material ui
Local State(HomeContainer)
We have accounts list and it is hard coded in HomeContainer. We are passing accounts constant to AccountList functional component. Instead, let us assign accounts constant value as an initial state value to HomeContainer. But why? — After we have received response from web service, just by updating the local state would render accounts in AccountList functional component.
const accounts = [
{ id: 1, title: 'Credit Account', desc: 'Credit Card Account', total: 3333 },
{ id: 2, title: 'My Wallet', desc: 'My Personal Waller', total: 30 },
{ id: 3, title: 'Bank Account', desc: 'My Savings Account', total: 10000 },
];
/* eslint-disable react/prefer-stateless-function */
export class HomeContainer extends React.Component {
constructor(props) {
super(props);
this.state = { accounts: [...accounts] };
}
render() {
return (
<div>
<AccountList accounts={this.state.accounts} />
</div>
);
}
}
Whenever there is a change in HomeContainer’s local state, AccountList will get the latest accounts and show in UI.
API Details
- URL : http://localhost:8080/accounts
- HTTP Request Method : GET
- Http Response :
- content : contains array of accounts
- pageable: contains pagination information and we are going to use this in this post
- other params : we wont use them in this post
Fetch API
fetch() is a method from window object(In global scope) and we can access fetch() method without import. fetch returns promise. Please check MDN documentation for detailed usage.
Let’s Fetch Accounts List
Let us write an arrow function, fetchAccounts, to fetch accounts.
fetchAccounts = () => {
};
fetch returns promise.
fetchAccounts = () => {
fetch('http://localhost:8080/accounts')
};
let’s parse.
fetchAccounts = () => {
fetch('http://localhost:8080/accounts')
.then(response => response.json());
};
Let’s handle success response. On success, we are updating local state with content (Which is array of accounts).
fetchAccounts = () => {
fetch('http://localhost:8080/accounts')
.then(response => response.json())
.then(response => this.handleSuccessResponse(response))
};handleSuccessResponse = response => {
this.setState({ accounts: [...response.content] });
};
Let’s also handle failure cases. We are console logging the error here.
fetchAccounts = () => {
fetch('http://localhost:8080/accounts')
.then(response => response.json())
.then(response => this.handleSuccessResponse(response))
.catch(error => this.handleErrorResponse(error));
};handleErrorResponse = error => {
console.log('error :', error);
};
Where shall we invoke fetchAccounts method
componentDidMount method gets called when component is being mounted. We can start our fetch process from it.
componentWillMount() {
this.fetchAccounts();
}
Response’s Account object returns description instead of desc and also it doesn’t return total param. Let us update AccountListItem to show description as secondary information.
const AccountListItem = account => (
<ListItem>
<Avatar>
<ImageIcon />
</Avatar>
<ListItemText primary={account.title} secondary={account.description} />
</ListItem>
);
Let us run and see the output.
What would happen If the service is down?
HomeContainer local state is being assigned with accounts array constant. Let’s remove it and assign an empty array as an initial state for accounts.
constructor(props) {
super(props);
this.state = { accounts: [] };
}
Let’s stop service and run the app.
Local state of accounts array is empty now. As per our logic, we are iterating the accounts array in AccountList and rendering an AccountListItem for each account. An empty accounts array would not render any AccountListItem.
Console log will print the error message.
What happened So far?
- componentDidMount gets called when HomeContainer is about to mount and it calls fetchAccounts method
- fetchAccounts method uses fetch to retrieve accounts from server
- Response Parsing
- handleSuccessResponse extracts content from Response and assign it to HomeContainer’s local state.
- AccountList updates in UI
Checkout the final code from Github(data-fetch-end branch)
git clone -b data-fetch-end https://github.com/ragunathrajasekaran/em-ui.git