Table of Content | Read Next: Part 9: Set up the Node.js server to send search request to Elasticsearch and receive results
In this blog, we will be creating the client side of our Earthquakes app with React.
Review
The following is what we will build.
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.
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.
Elasticsearch retrieves relevant documents and sends the documents to the server. The server sends the documents to the client.
Upon receiving the documents, the client displays the results in the form of cards. Each card contains information about one earthquake.
Resources
Would you rather watch a video to learn this content? Click on the link below!
Want the code covered in this blog? Click on the link below to access it!
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.
//in terminal within the earthquake_app directory
npx create-react-app client
This command will create a React app called client
within the earthquake_app
directory.
Step 2: Install Axios
Once the react app is created, cd into the client
directory.
//in terminal within the earthquake_app directory
cd client
We will be using a library called axios
to send HTTP requests to our server.
Install axios
by executing the following command.
//in terminal earthquake_app/client
npm i axios
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).
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).
//in client/package.json
"proxy": "http://localhost:3001",
Your package.json
file should look like this(line 21):
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.
Replace the default content of App.js
with the following snippet:
//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.
Lines 1-3
We import all the dependencies that we will need to carry out desired tasks.
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.
Let's take a look at lines 38-46.
Line 40 renders the app name displayed on the page(red box).
Line 45 renders the directions for users(orange box).
Quake Type
Lines 51-62 create the drop down menu for quake types.
The user gets to choose from four options.
- Earthquake(line 58)
- Quarry Blast(line 59)
- Ice Quake(line 60)
- Explosion(line 61)
Each option has a value associated with it(red box).
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.
Magnitude
The next drop down menu allows the user to select the magnitude level.
Lines 65-77 create this drop down menu.
The user gets to choose from five options.
- 2.5+ (line 72)
- 5.5+ (line 73)
- 6.1+ (line 74)
- 7+ (line 75)
- 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.
Location
The user can also specify the location of earthquakes they are interested in.
Lines 80-90 create a form where the user can type in the city, state, or country of interest.
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.
Date range
The next drop down menu allows the user to select the date range.
Lines 93-104 create this drop down menu.
The user gets to choose from four options.
- Past 7 Days (line 100)
- Past 14 Days (line 101)
- Past 21 Days(line 102)
- 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.
Sort by Magnitude
The next drop down menu allows the user to sort the search results by descending or ascending level of magnitude.
Lines 107-116 create this drop down menu.
The user gets to choose from two options.
- Largest Magnitude First (line 114)
- 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.
Line 119
This line of code creates the search button.
We set it up so that when the button is clicked, it calls the sendSearchRequest
function.
Lines 13-34
The function sendSearchRequest
is defined here.
Lines 14-24
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.
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.
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.
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
.
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.
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.
- Type(line 133)
- Time(line 134)
- Location(line 135)
- Latitude(line 136)
- Longitude(line 137)
- Magnitude(line 138)
- Depth(line 139)
- Significance(line 140)
- 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.
//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.
//in terminal within the client directory
npm start
You will see that our client is shown on the browser.
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.
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.
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.
Move on to Part 9 to learn how to do just that!
Top comments (0)