React is a popular front-end JavaScript technology for creating interactive user interfaces. You can use the React library to build a robust and modern web/mobile application without having much consideration about the backend. You can build anything from a to-do list app to something gigantic app like Facebook. Today, I am going to take you through the steps for building the front-end of a contacts application. The app will be connected to a contacts
API endpoint for the backend. This tutorial should also help you get started with using Fetch
API with React.
Overview and Prerequisite
We’re going to cover two things in this tutorial.
- How to create a React application?
- How to make the frontend and the API endpoints meet?
The second point is something seldom covered in a React tutorial. We’re going to bridge that gap by creating a basic application that retrieves data from a contacts API server. For the purpose of demonstration, I am going to use a mock API service. However, you can make your application practically useful by using actual contact APIs such as Google API, Lusha’s contact API, etc. They’re free and practically useful too.
The code for this tutorial is available in my Github repository. I’ve also set up a live demo on codesandbox.
create-react-app
is my first preference tool for creating a React application from scratch. Install create-react-app
if you haven’t already.
npm install create-react-app
Now, create a new project as follows:
create-react-app contact-list
Start the server using yarn start
.
Structuring the Application
Here is what we want the application to look like. A search bar on top and the contact list takes up the rest of the screen. I will be using Bootstrap and Font Awesome icons for the styling.
Here is how I want our app to work. Initially, the view will retrieve all contacts from the server and display them. You can then use the search bar to search for contacts using their name or phone number.
We can split the entire structure into three components, two of which are reusable -- the App
component, the SearchBar
component and the ContactCard
component.
Here is how the app component is going to look like:
class App extends Component {
.
.
render() {
return(
<div className="row">
<div className="col-xs-12 col-sm-offset-3 col-sm-6">
<span className="title">Contacts</span>
<SearchBar />
<ul cl assName="list-group" id="contact-list">
<li className="list-group-item">
<ContactCard />
</li>
</ul>
</div>
</div>
)
}
Retrieving the Contacts from Contacts API
Let’s add some state. We’ve initialized the state
object which includes searchText
, searchResult
, and contactList
.
constructor(props) {
super(props);
this.state = {
searchText: '',
searchResult: [],
contactList: []
}
Next up, we’re going to retrieve all contacts from the server. I’ve set up a mock API at mockable.io to simulate a real contacts API. The API returns the following JSON when a GET
request is made. You can try it yourself.
{ "code": 200,
"contacts":[
{
"name": "Mark",
"surname": "Cook",
"gender": "male",
"address": "52 East Forest Rd. mRockford, MI 49341",
"age": 27,
"title": "mr",
"phone": "(396) 881 3396",
"birthday": {
"dmy": "09/08/1990",
"mdy": "08/09/1990",
"raw": 650246246
},
},
...
]
}
Since we need to retrieve the contacts when the component mounts for the first time, I am going to use the componentWillMount
lifecycle method. First, create a constant that holds the contacts
API.
const contactsAPI = 'https://demo1443058.mockable.io/codeproject_tutorial/api/contacts';
Now, define the componentWillMount()
method as follows:
componentWillMount() {
let init = {
method: 'GET',
headers: new Headers(),
mode: 'cors',
cache: 'default'
};
fetch(contactsAPI, init)
.then( response => response.json())
.then(
data => this.setState(
prevState => ({
contactList: [...data.contacts]
})
)
)
}
The fetch()
method accepts two parameters, the API URL and an optional object that you can use to define certain additional properties. fetch()
returns a promise and you can chain multiple promises as we did here. We first convert it into a JSON object and move the fetched data into the state so that we can use it in the render
method.
Displaying the Contact Data
We’ve ensured that the data is fetched from the contacts
API and stored in the state when the App component mounts. Now, we need to pass down the data to Contactcard
component.
<ul className="list-group" id="contact-list">
{ this.state.contactList().map(
(contact) =>
<li key={contact.email} className="list-group-item">
<ContactCard contact = {contact}/>
</li>
)
}
</ul>
The contact information is available at props.contacts
. You can destructure the props as I did below to make the code more readable. The data stored in contact.name
, contact.surname
, contact.phone
, contact.email
and contact.address
are being displayed. I’ve used some extra CSS styles and HTML to make the contact card look good.
const ContactCard = ({contact}) => {
return(
<div>
<div className="col-xs-4 col-sm-3">
<img src={contact.photo} alt={contact.name + ' ' + contact.surname}
className="img-responsive img-circle" />
</div>
<div className="col-xs-8 col-sm-9">
<span>{contact.name + ' ' + contact.surname}</span><br/>
<span title={contact.address}></span>
<span>{contact.address}</span><br/>
<span title={contact.phone}></span>
<span>{contact.phone}</span><br/>
<span title={contact.email}></span>
<span>{contact.email}</span><br/>
</div>
<div className="clearfix"></div>
</div>
)
}
Searching through the Contact List
For searching contacts, we already have a SearchBar
component. Here is an outline of how the search feature should be implemented:
- The
SearchBar
component hosts the UI for the search bar which includes an input control field. - When the input field changes, it triggers the
onChange
event. - The
onChange
event executes the parent component’s callback and the input control value is passed as an argument. - The parent component, which is the
App
component in our case, has a handleSearch
method.
const SearchBar = ({onSearch}) => {
const handleChange = (e) => {
onSearch(e.target.value);
}
return(
<div className="input-group ">
<input onChange = {handleChange} className="form-control"
type="search" placeholder="Search for a contact" id="example-search-input" />
<button className="btn btn-default" type="button">
<i className="fa fa-search"></i>
</button>
</div>
)
}
And here’s the props for the callback.
<SearchBar onSearch={this.handleSearch} />
For the search logic, there are many ways in which you could approach this. Here is the one which I think is easiest to implement.
- Clear the state of the
searchResult
array so that the results from the previous search don’t show up. - Each contact in the contact list is mapped using the map method and is passed as an argument to the
searchContact
function along with the searchText
. - The
searchContact
function checks whether the name
, email
or the phone
attribute of the contacts matches the searchText
. - JavaScript’s
String.prototype.search()
is used to compare the string
s and if a match is found, -1
is returned. - If a contact is matched, store it in the state.
Here’s the code for handleSearch()
and the logic for searching individual contacts.
handleSearch(searchText) {
this.setState({searchResult: [], searchText: searchText});
this.state.contactList.map(contact => {
if(searchContact(contact, searchText)) {
this.setState( prevState => ({
searchResult: [...prevState.searchResult, contact]
}), () => console.log(this.state.searchResult))
}
})
}
...
const searchContact = (contact, searchText) => (
contact.name.toLowerCase().search(searchText.toLowerCase()) != -1 ||
contact.surname.toLowerCase().search(searchText.toLowerCase()) != -1 ||
contact.phone.toString().search(searchText) != -1
)
Querying the Contacts API for the Search Term
Alternatively, you might want to query the API directly for the search term on each keystroke or after a submit button is pressed. This is more expensive, but if the contacts
API is like a telephone directory in the cloud, here is something you could do. I will encourage you to experiment this part yourself.
- Clear the state of the
searchResult
similar to what we did earlier. - Using
fetch
, send a GET
request to the server with the searchText
appended like this. - GET
https://api.example.com/person?firstName=`${searchText}`
- Store the response in
this.state.searchResult
If you need to search the surname and the phone number, you might have to make multiple API calls and append the results to the state.
We’re nearly done with contacts application. However, the search results don’t appear on screen yet. This is because we haven’t yet supplied the searchResult
to the ContactCard
component. We could do something like this:
<ul className="list-group" id="contact-list">
{ this.state.searchResult().map(
(contact) =>
<li key={contact.email} className="list-group-item">
<ContactCard contact = {contact}/>
</li>
)
}
</ul>
However, the component won’t fetch the contacts from the server when the component mounts. To fix this, create a new method or use a ternary operator that does the following:
returnContactList() {
return this.state.searchText ? this.state.searchResult :this.state.contactList
}
And let returnContactlist()
decide which list to return. Here is the final version of the code.
<ul className="list-group" id="contact-list">
{ this.returnContactList().map(
(contact) =>
<li key={contact.email} className="list-group-item">
<ContactCard contact = {contact}/>
</li>
)
}
</ul>
Voila! We have our own working contacts app built using React.
Summary
We’ve built a contact list application from scratch in about 30 minutes. That’s a remarkable feat on its own. Apart from that, we’ve also learned how to make API requests using React and use the returned data in a meaningful way. What are your thoughts about React as frontend library? Let me know them through the comments.