Part 8: Build the client side of the app with React
Tags: react, beginners, elasticsearch, webdev
Published: 2022-09-07 15:14:58

[Table of Content](https://dev.to/lisahjung/beginners-guide-to-building-a-full-stack-app-nodejs-react-with-elasticsearch-5347) | [Read Next: Part 9: Set up the Node.js server to send search request to Elasticsearch and receive results](https://dev.to/lisahjung/part-9-set-up-the-nodejs-server-to-handle-elasticsearch-requests-5206)

In this blog, we will be creating the client side of our Earthquakes app with React. 

### Review
The following is what we will build. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ysz97wbvk1mnffjpd3oi.png)

Our client allows the user to search for earthquakes based on quake type, magnitude, location, and date range. It also allows the user to sort the search results by ascending or descending order of magnitude. 

![ezgif com-gif-maker (5)](https://user-images.githubusercontent.com/60980933/169545964-9c84ea5f-94f7-47cb-9ba8-64b5973a0b6a.gif)

When the user hits the search button, the user input is sent to the server via HTTP request. 

The server passes the user input into a Elasticsearch request and sends the request to Elasticsearch. 

![ezgif com-gif-maker (1)](https://user-images.githubusercontent.com/60980933/161301742-6b2ca0a1-2c5b-43b6-b8bd-cdf16605f80f.gif)

Elasticsearch retrieves relevant documents and sends the documents to the server. The server sends the documents to the client.  

![ezgif com-gif-maker](https://user-images.githubusercontent.com/60980933/161300981-5ec91b21-2a79-48f4-aff5-0a0b362ad5d0.gif)

Upon receiving the documents, the client displays the results in the form of cards. Each card contains information about one earthquake.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/518nqvd380gv6fzk0uhx.png)

###Resources

*Would you rather watch a video to learn this content? Click on the link below!*
- [Episode 8: Build the client with React](https://www.youtube.com/watch?v=53071vyu7UY)

*Want the code covered in this blog? Click on the link below to access it!*
- [GitHub repo for part 8](https://github.com/LisaHJung/beginners-guide-to-creating-a-full-stack-Javascript-app-with-Elasticsearch/tree/5-build_the_client)  

###Building the client 
Throughout the blog series, we have been working with the `earthquake_app` directory. In this directory, we have added the server side code under the `server` directory.

In this blog, we will be adding the client side code to the `earthquake_app` directory.

####Step 1: Create a React app
*Open a new tab in the terminal.*

*cd into the `earthquake_app` directory and execute the following command.* 

```javascript
//in terminal within the earthquake_app directory
npx create-react-app client
``` 
Screenshot of the terminal:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k2nbeippgi9rhpjchdck.png)

This command will create a React app called `client` within the `earthquake_app` directory.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a0uqbtpdwk3hin9r3wup.png)
 
####Step 2: Install Axios

*Once the react app is created, cd into the `client` directory.*
```javascript
//in terminal within the earthquake_app directory
cd client
```
Screenshot of the terminal:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uuc7xw6o7awt2np10ba8.png)
 
We will be using a library called `axios` to send HTTP requests to our server.

*Install `axios` by executing the following command.*

```javascript
//in terminal earthquake_app/client
npm i axios
``` 
Screenshot of the terminal:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/km9s68padn89i4vzzyif.png)
 
*From the code editor, expand the `client` directory to locate the `package.json` file.* 

You will see that the library called `axios` has been installed(*line 9*).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/osyjffbr5tu61vbpebbc.png)
 
####Step 3: Set up the proxy to our server.
In the same file, we will add the "proxy" key and point the proxy at localhost:3001 where our Node.js server is running. 

*Add the following code between the "scripts" object(line 20) and the "eslintConfig" object(line 21).*

```javascript
//in client/package.json
"proxy": "http://localhost:3001",
```
Your `package.json` file should look like this(*line 21*):
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4fos1f0hgw68dkye3c8.png)
 
####Step 4: Build the client 
*When you look within the `client` directory, you will see the `src` directory. Expand this directory to access the `App.js` file.*

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rlb8jw3zju1ppwq1t3vy.png)
 
*Replace the default content of `App.js` with the following snippet:*

```javascript
//in client/src/App.js
import axios from 'axios';
import { useState } from 'react';
import './App.css';

const App = () => {
  const [chosenType, setChosenType] = useState(null);
  const [chosenMag, setChosenMag] = useState(null);
  const [chosenLocation, setChosenLocation] = useState(null);
  const [chosenDateRange, setChosenDateRange] = useState(null);
  const [chosenSortOption, setchosenSortOption] = useState(null);
  const [documents, setDocuments] = useState(null);

  const sendSearchRequest = () => {
    const results = {
      method: 'GET',
      url: 'http://localhost:3001/results',
      params: {
        type: chosenType,
        mag: chosenMag,
        location: chosenLocation,
        dateRange: chosenDateRange,
        sortOption: chosenSortOption,
      },
    };
    axios
      .request(results)
      .then((response) => {
        console.log(response.data);
        setDocuments(response.data);
      })
      .catch((error) => {
        console.error(error);
      });
  };

  return (
    <div className='app'>
      <nav>
        <ul className='nav-bar'>
          <li>Earthquake Watch</li>
        </ul>
      </nav>
      <p className='directions'>
        {' '}
        Search for earthquakes using the following criteria:
      </p>
      <div className='main'>
        <div className='type-selector'>
          <ul>
            <li>
              <select
                name='types'
                id='types'
                value={chosenType}
                onChange={(e) => setChosenType(e.target.value)}
              >
                <option value={null}>Select a Type</option>
                <option value='earthquake'>Earthquake</option>
                <option value='quarry blast'>Quarry Blast</option>
                <option value='ice quake'>Ice Quake</option>
                <option value='explosion'>Explosion</option>
              </select>
            </li>
            <li>
              <select
                name='mag'
                id='mag'
                value={chosenMag}
                onChange={(e) => setChosenMag(e.target.value)}
              >
                <option value={null}>Select magnitude level</option>
                <option value='2.5'>2.5+</option>
                <option value='5.5'>5.5+</option>
                <option value='6.1'>6.1+</option>
                <option value='7'>7+</option>
                <option value='8'>8+</option>
              </select>
            </li>
            <li>
              <form>
                <label>
                  <input
                    className='form'
                    type='text'
                    placeholder='Enter city, state, country'
                    value={chosenLocation}
                    onChange={(e) => setChosenLocation(e.target.value)}
                  />
                </label>
              </form>
            </li>
            <li>
              <select
                name='dateRange'
                id='dateRange'
                value={chosenDateRange}
                onChange={(e) => setChosenDateRange(e.target.value)}
              >
                <option value={null}>Select date range</option>
                <option value='7'>Past 7 Days</option>
                <option value='14'>Past 14 Days</option>
                <option value='21'>Past 21 Days</option>
                <option value='30'>Past 30 Days</option>
              </select>
            </li>
            <li>
              <select
                name='sortOption'
                id='sortOption'
                value={chosenSortOption}
                onChange={(e) => setchosenSortOption(e.target.value)}
              >
                <option value={null}>Sort by</option>
                <option value='desc'>Largest Magnitude First</option>
                <option value='asc'>Smallest Magnitude First</option>
              </select>
            </li>
            <li>
              <button onClick={sendSearchRequest}>Search</button>
            </li>
          </ul>
        </div>
        {documents && (
          <div className='search-results'>
            {documents.length > 0 ? (
              <p> Number of hits: {documents.length}</p>
            ) : (
              <p> No results found. Try broadening your search criteria.</p>
            )}
            {documents.map((document) => (
              <div className='results-card'>
                <div className='results-text'>
                  <p>Type: {document._source.type}</p>
                  <p>Time: {document._source['@timestamp']}</p>
                  <p>Location: {document._source.place}</p>
                  <p>Latitude: {document._source.coordinates.lat}</p>
                  <p>Longitude: {document._source.coordinates.lon}</p>
                  <p>Magnitude: {document._source.mag}</p>
                  <p>Depth: {document._source.depth}</p>
                  <p>Significance: {document._source.sig}</p>
                  <p>Event URL: {document._source.url}</p>
                </div>
              </div>
            ))}
          </div>
        )}
      </div>
    </div>
  );
};

export default App;
```
Let's go over the code. 

**Heads up!** 

For reference purposes only, I have included screenshots of code that I will be explaining. 

If you need to copy and paste the code, please refer to the code snippet above or the [GitHub repo for part 8](https://github.com/LisaHJung/beginners-guide-to-creating-a-full-stack-Javascript-app-with-Elasticsearch/tree/5-build_the_client).

*Lines 1-3*
We import all the dependencies that we will need to carry out desired tasks. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no5xok6p1r7racc8iuf9.png)
 
*Line 1* 
We import `axios` to send HTTP request to the server. 

*Line 2*
We import `useState` hook to manage state. 

*Line 3*
We import `App.css` to apply styling to the page. 

To explain the code in a logical manner, I may skip around a little bit so be sure to pay attention to the line numbers.

### Set up the client to display a list of search options and send user input to the server

*Lines 36-149*
These lines of code are responsible for what the users see when they first interact with our app.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uedbw9boydrnmqp4libj.png)
 
Let's take a look at *lines 38-46*.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/icdxeysdehxbhmcljwtz.png)
  
*Line 40* renders the app name displayed on the page(red box). 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0i60mi2q9bs1e5zbm8j6.png)
 
*Line 45* renders the directions for users(orange box).

**Quake Type**
*Lines 51-62* create the drop down menu for quake types. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/swfqf9pkto0u1ynen3mk.png)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nv47pq3u4ni1oc5ye6d5.png)
 
The user gets to choose from four options. 
1. Earthquake(*line 58*)
2. Quarry Blast(*line 59*)
3. Ice Quake(*line 60*)
4. Explosion(*line 61*)

Each option has a value associated with it(red box).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dbzw3t81fbq0aa3gy3tn.png)
 
*Lines 54-55* 
When a user chooses an option from the select control, its value(red box above) is set as state for the `chosenType` variable.

The state variable for `chosenType` is defined in *line 6*. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/19r7estt69eod9lcfica.png)
 
**Magnitude**
The next drop down menu allows the user to select the magnitude level. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/twmqpjahb8c8e3fem36p.png)
 
*Lines 65-77* create this drop down menu. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0uqr2jphgf57u66vj2kz.png)

The user gets to choose from five options. 
1. 2.5+ (*line 72*)
2. 5.5+ (*line 73*)
3. 6.1+ (*line 74*)
4. 7+ (*line 75*)
5. 8+ (*line 76*)

*Lines 68-69*
When a user chooses an option from the select control, its value is set as state for the `chosenMag` variable. 

The state variable for `chosenMag` is defined in *line 7*. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/23j1ddlgvsfq9pjfaxiu.png)

**Location**
The user can also specify the location of earthquakes they are interested in. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ikbb7wa6kr9grjpm50dj.png)
 
*Lines 80-90* create a form where the user can type in the city, state, or country of interest.  

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1jvalzuixrfop5nu17ke.png)

*Lines 86-87*
When a user types in the location, user input is set as state for the `chosenLocation` variable.

The state variable for `chosenLocation` is defined in line 8. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/stjv4c5fhau7udxfzl9i.png)

**Date range**
The next drop down menu allows the user to select the date range.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ob3jayccir5ube8rw3mf.png)
  
*Lines 93-104* create this drop down menu. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02xrhd7kxjbf5ykalm69.png)

The user gets to choose from four options. 
1. Past 7 Days (*line 100*)
2. Past 14 Days (*line 101*)
3. Past 21 Days(*line 102*)
4. Past 30 Days (*line 103*)

*Lines 96-97*
When a user chooses an option from the select control, its value is set as state for the `chosenDateRange` variable. 

The state variable for `chosenDateRange` is defined in line 9. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5ybx3o44fhstgehk4a0.png)

**Sort by Magnitude**
The next drop down menu allows the user to sort the search results by descending or ascending level of magnitude. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/528vqd4phoq7yhtsg3gs.png)

*Lines 107-116* create this drop down menu. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/edlc5ny3800itcyd0wqd.png)

The user gets to choose from two options. 
1. Largest Magnitude First (*line 114*)
2. Smallest Magnitude First (*line 115*)

*Lines 110-111*
When a user chooses an option from the select control, its value is set as state for the `chosenSortOption` variable. 

The state variable for `chosenSortOption` is defined in line 10.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y05bxkx38b8v5zttyw63.png)
 
*Line 119*
This line of code creates the search button. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhrmbadyurpyegm3u6bz.png)

We set it up so that when the button is clicked, it calls the `sendSearchRequest` function. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l0jmelgd25ebnasdb4io.png)

*Lines 13-34*
The function `sendSearchRequest` is defined here.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sb6akp6dox73ltny7de7.png)

*Lines 14-24*

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jvi0avro1kezm29db34n.png)

In *line 14*, we create a constant called `results`. 

We specify that we want to send a get(*line 15*) request to our server(localhost:3001) with a url ending in `/results`(*line 16*).

*Lines 17-23*
Within this request, we pass the params(the user input) that were collected. 

*Lines 25-33*
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cc9g6zqvqka1ksgv0i9n.png)
 
We use `axios`(*line 25*) to send the get request to our server(*line 26*).

Remember, when the client sends the user input to the server, the server will pass the user input into a Elasticsearch request and send the request to Elasticsearch.

![ezgif com-gif-maker (1)](https://user-images.githubusercontent.com/60980933/161301742-6b2ca0a1-2c5b-43b6-b8bd-cdf16605f80f.gif)

Elasticsearch will retrieve relevant documents and send these documents to the server. Upon receiving these documents, the server will send the results to the client. 

![ezgif com-gif-maker](https://user-images.githubusercontent.com/60980933/161300981-5ec91b21-2a79-48f4-aff5-0a0b362ad5d0.gif)

When the client receives a response (i.e. search results) from the server, the client will print the response to the console(*line 28*).

*Line 11*
In *line 11*, we defined the state variable `documents`. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2tpcs7hxabfkhi92650h.png)

*Line 29*
The client will update the state variable `documents` to include the documents received from the server. 

*Lines 31-33*
If an error were to occur, we print the error in the console.

So far, we have discussed how we can display a list of search options for the user and how to capture the user input and send it to the server.

Next, we are going to focus on how the client should handle the documents that were retrieved from Elasticsearch. 

### Set up the client to handle documents received from the server
*Lines 123-146* 
These lines of code handle documents received from the server. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/s0zop4x0hci6blsmj5u2.png)
  
These lines of code are only rendered when documents are received from the server. 

*Lines 125-129* 
We set up a ternary operator to specify that when the number of documents is greater than 0, the number of documents is displayed on the screen. 

If the number of documents returned are 0, then display the message "No results found. Try broadening your search criteria."

*Lines 130-144*
The client runs through the documents(*line 130*) and creates a card(*line 131 -143*) for each document in the array. Each card displays the following information about each earthquake. 

1. Type(*line 133*)
2. Time(*line 134*)
3. Location(*line 135*)
4. Latitude(*line 136*)
5. Longitude(*line 137*)
6. Magnitude(*line 138*)
7. Depth(*line 139*)
8. Significance(*line 140*)
9. Event URL(*line 141*)

The information is accessed by document._source.`name_of_the_field`.  

#### Step 5: Add styling to the page. 
Open the `App.css` file located within the `src` directory.

Replace the default content with the following code snippet.
```javascript
//in client/src/App.css
@import url('https://fonts.googleapis.com/css2?family=Open+Sans+Condensed:wght@300&display=swap');

* {
  font-family: 'Open Sans Condensed', sans-serif;
}

body {
  background-image: url(https://i.imgur.com/lx59qEN.jpg);
  background-position-x: center;
  background-position-y: center;
  background-size: 100% auto;
  background-repeat: no-repeat;
  background-attachment: fixed;
  background-origin: initial;
  background-clip: initial;
  margin: 0;
}

div {
  color: white;
}

.nav {
  width: 100%;
  float: left;
}

.nav-bar {
  margin: 0;
  padding: 8px;
  height: 50px;
  background-color: rgb(28, 28, 28);
}

.nav-bar li {
  display: inline;
  margin-top: 0px;
  font-size: 2.5em;
}

.directions {
  margin-left: 14px;
  font-size: 1.5em;
}

.main {
  display: flex;
  flex-direction: column;
  font-family: inherit;
}
.type-selector {
  margin-left: -30px;
}

.type-selector li {
  float: left;
  display: inline;
  margin-left: 4px;
  margin-top: -19px;
  font-style: inherit;
}

.type-selector li:focus,
.type-selector li:hover {
  outline: none;
  border: 1px solid #bbbbbb;
}

.form {
  width: 200px;
  height: 16px;
}

.form::placeholder {
  color: black;
  opacity: 1;
}

.type-selector button:hover {
  color: #be3400;
  border: 0.1rem #404040 solid; 
}

.type-selector button {
  height: 23px;
}

.search-results {
  display: block;
  width: 100%;
  margin-left: 13px;
  font-family: inherit;
}

.results-card {
  background-color: rgb(25, 129, 67, 0.7);
  flex: 0 1 29rem;
  height: 23rem;
  border-radius: 10px;
  font-weight: bold;
  border: 1px;
  float: left;
  margin: 1px;
  margin-top: 5px;
  margin-right: 10px;
  padding: 10px epx;
  justify-content: space-between;
  max-width: 400px;
}

.results-text {
  margin-left: 2rem;
  margin-right: 2rem;
}
```
This code snippet will create the styling seen in the UI. Since this is  Elasticsearch and Kibana tutorial, we will not go over this in detail.

**Step 6: Start the client**
*Locate the tab of the terminal where we created the client.*

*Start your client by executing the following command in the terminal.* 
```javascript
//in terminal within the client directory
npm start
```
Screenshot of terminal:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eycnkpfiqqkys18ilmgs.png)
 
You will see that our client is shown on the browser.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sm4qdhonx94dpjmw8z1m.png)

When you click on each select option, it will render the corresponding drop down menu. It will also display a form where a user can type in the location of interest. 

![ezgif com-gif-maker (5)](https://user-images.githubusercontent.com/60980933/169545964-9c84ea5f-94f7-47cb-9ba8-64b5973a0b6a.gif)

**Summary** 
In this blog, we have created a UI, set up our client to capture user input and send the input to the server.

The client is also set up to receive search results from the server and display the results in the form of cards. 

**What's next?**

In the next blog, we will set up our server to pass the user input received from the client into a Elasticsearch request and send it to Elasticsearch.

![ezgif com-gif-maker (1)](https://user-images.githubusercontent.com/60980933/161301742-6b2ca0a1-2c5b-43b6-b8bd-cdf16605f80f.gif)

Elasticsearch will then retrieve the relevant documents and send the documents to the server. 

We will set up our server to receive the documents and send the documents to the client so the results could be displayed to the user.

![ezgif com-gif-maker](https://user-images.githubusercontent.com/60980933/161300981-5ec91b21-2a79-48f4-aff5-0a0b362ad5d0.gif)

Move on to [Part 9](https://dev.to/lisahjung/part-9-set-up-the-nodejs-server-to-handle-elasticsearch-requests-5206) to learn how to do just that!