How to Build a Search Filter using React and React Hooks
In today‘s data-driven world, search filters have become an essential feature in web applications. They allow users to quickly find relevant information from a large dataset, improving the overall user experience. In this article, we‘ll explore how to build a search filter using React and React hooks.
Setting up the Project
Before we dive into the implementation, let‘s set up a new React project. Open your terminal and run the following command:
npx create-react-app search-filter-demo
cd search-filter-demo
This will create a new React project named "search-filter-demo" and navigate you into the project directory.
Next, we‘ll install Axios, a popular library for making HTTP requests in JavaScript. Run the following command in your terminal:
npm install axios
Fetching Data from an API
For this example, we‘ll use the JSONPlaceholder API to fetch a list of users. Create a new file called App.js
in the src
directory and add the following code:
import React, { useState, useEffect } from ‘react‘;
import axios from ‘axios‘;
function App() {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchUsers = async () => {
const response = await axios.get(‘https://jsonplaceholder.typicode.com/users‘);
setUsers(response.data);
};
fetchUsers();
}, []);
return (
<div>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default App;
In this code, we use the useState
hook to create a state variable called users
, which will store the list of users fetched from the API. We also use the useEffect
hook to make an asynchronous request to the API using Axios when the component mounts.
Once the data is fetched, we update the users
state using the setUsers
function, which triggers a re-render of the component. Finally, we display the list of users in the UI using the map
function.
Creating the Search Input Component
Now, let‘s create a search input component that will allow users to enter a search query. Create a new file called SearchInput.js
in the src
directory and add the following code:
import React from ‘react‘;
function SearchInput({ searchQuery, setSearchQuery }) {
return (
<input
type="text"
placeholder="Search users..."
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
/>
);
}
export default SearchInput;
In this component, we accept two props: searchQuery
and setSearchQuery
. The searchQuery
prop represents the current search query entered by the user, and the setSearchQuery
prop is a function that updates the search query state in the parent component.
We render an input
element with a type
of "text" and a placeholder
of "Search users…". We also bind the value
prop to the searchQuery
state and the onChange
event to the setSearchQuery
function, which allows us to update the search query state whenever the user types in the input field.
Implementing the Search Functionality
Now that we have the search input component, let‘s implement the search functionality in the App
component. Update the App.js
file with the following code:
import React, { useState, useEffect } from ‘react‘;
import axios from ‘axios‘;
import SearchInput from ‘./SearchInput‘;
function App() {
const [users, setUsers] = useState([]);
const [searchQuery, setSearchQuery] = useState(‘‘);
useEffect(() => {
const fetchUsers = async () => {
const response = await axios.get(‘https://jsonplaceholder.typicode.com/users‘);
setUsers(response.data);
};
fetchUsers();
}, []);
const filteredUsers = users.filter(user =>
user.name.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<div>
<SearchInput searchQuery={searchQuery} setSearchQuery={setSearchQuery} />
<ul>
{filteredUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default App;
Here‘s what we‘ve changed:
- We added a new state variable called
searchQuery
to store the current search query entered by the user. - We imported the
SearchInput
component and rendered it inside theApp
component, passing thesearchQuery
andsetSearchQuery
props. - We created a new variable called
filteredUsers
, which filters theusers
array based on thesearchQuery
. We use thefilter
method to create a new array that only includes users whose names contain the search query (case-insensitive). - Finally, we update the
map
function to iterate over thefilteredUsers
array instead of theusers
array, so that only the filtered users are displayed in the UI.
Optimizing the Search Filter
While the current implementation works, there are a few optimizations we can make to improve performance and user experience:
-
Debounce: When the user types quickly in the search input, the
filter
function is called on every keystroke, which can be expensive for large datasets. We can use a debounce function to delay the execution of thefilter
function until the user has stopped typing for a certain amount of time (e.g., 300ms). -
Case-insensitive search: In the current implementation, we convert both the user‘s name and the search query to lowercase before comparing them. This ensures that the search is case-insensitive. However, we can optimize this further by using a regular expression with the
i
flag, which performs a case-insensitive search.
Here‘s an updated version of the App
component with these optimizations:
import React, { useState, useEffect } from ‘react‘;
import axios from ‘axios‘;
import SearchInput from ‘./SearchInput‘;
function debounce(func, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func(...args), delay);
};
}
function App() {
const [users, setUsers] = useState([]);
const [searchQuery, setSearchQuery] = useState(‘‘);
useEffect(() => {
const fetchUsers = async () => {
const response = await axios.get(‘https://jsonplaceholder.typicode.com/users‘);
setUsers(response.data);
};
fetchUsers();
}, []);
const filteredUsers = users.filter(user =>
new RegExp(searchQuery, ‘i‘).test(user.name)
);
const debouncedSetSearchQuery = debounce(setSearchQuery, 300);
return (
<div>
<SearchInput searchQuery={searchQuery} setSearchQuery={debouncedSetSearchQuery} />
<ul>
{filteredUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default App;
In this updated version, we:
- Created a
debounce
function that takes a function and a delay as arguments and returns a new function that delays the execution of the original function by the specified delay. - Updated the
filteredUsers
variable to use a regular expression with thei
flag for case-insensitive search. - Created a new variable called
debouncedSetSearchQuery
that wraps thesetSearchQuery
function with thedebounce
function, specifying a delay of 300ms. - Updated the
SearchInput
component to use thedebouncedSetSearchQuery
function instead of thesetSearchQuery
function.
With these optimizations, the search filter will perform better and provide a smoother user experience, especially for large datasets.
Styling the Search Filter
To make the search filter visually appealing, we can add some basic styling using CSS. Create a new file called App.css
in the src
directory and add the following code:
.app {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
h1 {
text-align: center;
}
input {
width: 100%;
padding: 10px;
font-size: 16px;
margin-bottom: 20px;
}
ul {
list-style-type: none;
padding: 0;
}
li {
background-color: #f4f4f4;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
}
In this CSS file, we:
- Set a max-width for the
app
class and center it horizontally usingmargin: 0 auto
. - Center the
h1
heading usingtext-align: center
. - Style the search input to take up the full width of its container, add some padding, and set a font size.
- Remove the default list styling from the
ul
element usinglist-style-type: none
and remove the default padding. - Style the
li
elements with a light gray background color, some padding, margin bottom, and rounded corners usingborder-radius
.
Now, update the App.js
file to import the CSS file and apply the app
class to the top-level div
:
import React, { useState, useEffect } from ‘react‘;
import axios from ‘axios‘;
import SearchInput from ‘./SearchInput‘;
import ‘./App.css‘;
// ... rest of the code ...
function App() {
// ... rest of the code ...
return (
<div className="app">
<SearchInput searchQuery={searchQuery} setSearchQuery={debouncedSetSearchQuery} />
<ul>
{filteredUsers.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default App;
With these changes, the search filter should now have a more polished and user-friendly appearance.
Conclusion
In this article, we learned how to build a search filter using React and React hooks. We covered the following topics:
- Setting up a React project and installing necessary dependencies
- Fetching data from an API using Axios and storing it in state
- Creating a search input component and handling user input
- Implementing the search functionality using React hooks (useState and useEffect)
- Filtering the data based on the search query and updating the UI
- Optimizing the search filter for better performance using debounce and regular expressions
- Styling the search filter component using CSS
By following the steps outlined in this article, you should now have a solid understanding of how to build a search filter in React. Remember to keep performance and user experience in mind when implementing search filters, and don‘t hesitate to experiment with different optimization techniques and styling options to create the best possible search experience for your users.
Happy coding!