This was what we achieved in part 2 of the tutorial.
Our application is working as it should but what if there was a hundred, a thousand or even one million user profiles?
Searching for a specific user would be akin to looking for a needle in a haystack.
We can solve this problem by adding search functionalities.
To do that, we first install the js-search and axios plugin.
$ npm install --save js-search axios
Add a SearchContainer.js file inside src/components folder.
$ touch src/components/SearchContainer.js
Paste the following code in SearchContainer.js.
import React, { Component } from "react"
import Axios from "axios"
import * as JsSearch from "js-search"
import Profile from "./profile"
class Search extends Component {
state = {
peopleList: [],
search: [],
searchResults: [],
isLoading: true,
isError: false,
searchQuery: "",
}
/**
* React lifecycle method to fetch the data
*/
async componentDidMount() {
Axios.get("https://randomuser.me/api/?results=10")
.then(result => {
const peopleData = result.data
this.setState({
peopleList: peopleData.results,
})
this.rebuildIndex()
})
.catch(err => {
this.setState({ isError: true })
console.log(`${err}`)
})
}
/**
* rebuilds the overall index based on the options
*/
rebuildIndex = () => {
const { peopleList } = this.state
const dataToSearch = new JsSearch.Search("results")
/**
* defines a indexing strategy for the data
* more more about it in here https://github.com/bvaughn/js-search##configuring-the-index-strategy
*/
dataToSearch.indexStrategy = new JsSearch.PrefixIndexStrategy()
/**
* defines the sanitizer for the search
* to prevent some of the words from being excluded
*
*/
dataToSearch.sanitizer = new JsSearch.LowerCaseSanitizer()
/**
* defines the search index
* read more in here https://github.com/bvaughn/js-search##configuring-the-search-index
*/
// dataToSearch.searchIndex = new JsSearch.TfIdfSearchIndex("email")
dataToSearch.addIndex('email') // sets the index attribute for the data
dataToSearch.addIndex("phone") // sets the index attribute for the data
dataToSearch.addDocuments(peopleList) // adds the data to be searched
this.setState({ search: dataToSearch, isLoading: false })
}
/**
* handles the input change and perfom a search with js-search
* in which the results will be added to the state
*/
searchData = e => {
const { search } = this.state
const queryResult = search.search(e.target.value)
this.setState({ searchQuery: e.target.value, searchResults: queryResult })
}
handleSubmit = e => {
e.preventDefault()
}
render() {
const { peopleList, searchResults, searchQuery } = this.state
const queryResults = searchQuery === "" ? peopleList : searchResults
return (
<div>
<div>
<form onSubmit={this.handleSubmit}>
<div>
<span>Search</span>
</div>
<input type="text" value={searchQuery} onChange={this.searchData} placeholder="Enter your search here" />
</form>
</div>
Number of items:
{queryResults.length}
<div>
<table>
<thead>
<tr>
<th>
Name
</th>
<th>
Gender
</th>
<th>
Email
</th>
<th>
Number
</th>
<th>
Image
</th>
</tr>
</thead>
<tbody>
{queryResults.map(function(item, index) {
return (
<Profile props={item} key={index} />
)
})}
</tbody>
</table>
</div>
</div>
)}
}
export default Search
The code may look confusing but it is actually quite straightforward.
Let's refactor our existing code.
Edit index.js.
...
import { graphql } from "gatsby"
import Search from "../components/SearchContainer"
...
...
export default ({ data }) => {
return (
<div>
<Search />
</div>
)
}
Replace the code for profile.js with the following.
import React from "react"
import { Link } from "gatsby"
export default ({props}, index) => {
return (
<tr key={`row_${props.email}`}>
<th>
<Link to={`/details/`} state={{props}}>{props.name.title} {props.name.first} {props.name.last}</Link>
</th>
<td>{props.gender}</td>
<td>{props.email}</td>
<td>{props.phone}</td>
<td>
<Link to={`/details/`} state={{props}}><img src={props.picture.thumbnail} alt={`${props.name.title} ${props.name.first} ${props.name.last}`} /></Link>
</td>
</tr>
)
}
Add a details.js component inside src/components.
$ touch ./src/components/details.js
Paste the following code into src/components/details.js.
import React from "react"
import { Link } from "gatsby"
export default ({props}) => {
return (
<div>
<div>
<img src={props.picture.large} alt={`${props.name.title} ${props.name.first} ${props.name.last}`} />
<div>
<h5>{props.name.title} {props.name.first} {props.name.last}</h5>
<ul>
<li><strong>Gender:</strong> {props.gender}</li>
<li><strong>Email:</strong> {props.email}</li>
<li><strong>Address:</strong> {props.location.street}, {props.location.city}, {props.location.state}</li>
</ul>
</div>
</div>
<div>
<Link to="/">Back to Home</Link>
</div>
</div>
)
}
Refactor our src/pages/detail.js page to include the new component.
import React from "react"
import Details from "../components/details"
export default ({ location }) => {
const value = location.state.props
return (
<div>
<Details props={value} />
</div>
)
}
Test the search feature by entering a user's email or phone number in the search input field.
Pretty cool but our application is lacking in the aesthetics department.
In the final part of this tutorial, we will style the site using CSS Modules.