I was working on a small project and needed the ability for users to search and also filter through the database. I had some hiccups working on the filter feature and in the course of my research, I did not come across any helpful article to assist me. I was able to resolve the issue and chose to write about the process π
In this article, I'll walk us on how to build a Filter Component in React. If you want to learn how to build a Search Component check my article here.
This article is a continuation of the search article that covers setting up React, installation of axios
, displaying data from the api
using axios
, and of course, building the search
component. We used https://restcountries.com/ API to fetch countries.
I'm assuming you already know how to do the aforementioned, if not I strongly advise you to go through the article.
To get started, we create a Filter component in the Components folder. We use the select
element to create options we can filter with. In this case, we would filter by continents.
import React from 'react';
const Filter = () => {
return (
<>
<select name='country' id='country'>
<option value='All'>Filter by Continent</option>
<option value='Antarctica'>Antarctica</option>
<option value='Asia'>Asia</option>
<option value='Africa'>Africa</option>
<option value='Europe'>Europe</option>
<option value='North America'>North America</option>
<option value='South America'>South America</option>
<option value='Oceania'>Oceania</option>
</select>
</>
);
};
export default Filter;
Now we need a function to handle our filter.
In App.js, we import the Filter component, then we create a function called handleFilter
which is passed as a prop to the Filter component with e.target.value
as an argument. The state of filter(setFilterV
) is updated to whatever the user selects as an option.
import Filter from './Components/Filter';
const [filterV, setFilterV] = useState('All');
const handleFilter = (filterValue) => {
setFilterV(filterValue)
};
<Filter onChange={(e)=>handleFilter(e.target.value)}/>
In the Filter component, we pass onChange
as a prop
const Filter = ({ onChange }) => {
return (
<>
<select name='country' id='country' onChange={onChange}>
<option value='All'>Filter by Continent</option>
<option value='Antarctica'>Antarctica</option>
<option value='Asia'>Asia</option>
<option value='Africa'>Africa</option>
<option value='Europe'>Europe</option>
<option value='North America'>North America</option>
<option value='South America'>South America</option>
<option value='Oceania'>Oceania</option>
</select>
</>
);
};
To test if this works, we can console.log(filterV)
and inspect the result in our console.
Still, on our handleFilter
function, we need to filter through our countries array and return countries with continents that match the one in our filter array. Since country.continent
is an array we can use the javascript method .includes
to return exact matches.
const handleFilter = (filterValue) => {
console.log(filterValue);
setFilterV(filterValue);
const filterCountry = countries.filter((country) =>
country.continents.includes(filterValue)
);
};
Now we have to make a little adjustment to our Search component. We create a new state called filteredResults
const [filteredResult, setFilteredResult] = useState([])
Then we refactor our code and join both the handleSearch
function and filteredSearch
function into one function. This is similar to the way we wrote the handleFilter
function.
const filteredSearch = (searchValue)=>{
setSearch(searchValue);
const searchAll = countries.filter((country) =>
country.name.common.toLowerCase().includes(search.toLowerCase())
);
setFilteredResult(searchAll)
}
In the Search component we passfilteredSearch
to the onChange
event as props with e.target.value
as an argument.
<Search onChange={(e)=>filteredSearch(e.target.value)} value={search} />
Back on the handleFilter
function, we also update filteredResult
state with filterCountry
setFilteredResult(filterCountry);
Now the only thing left to do is to change the variable we loop through to display the appropriate data.
{filteredSearch.map(country=><Country key={country.cca3} country={country} />)}
becomes
{filteredResult.map(country=><Country key={country.cca3} country={country} />)}
In the Render part of App.js
we have to make a few changes so that the application renders our filtered search. We set a condition to loop through filteredResult
when the value of filter
is not equal to All
and to display all countries when the value of filter
is equal to All
.
{filter !== 'All' ?
filteredResult.map(country=>
<Country key={country.cca3} country={country} />)
: countries.map(country=>
<Country key={country.cca3} country={country} />)
}
If you are following from the search article then the render changes to this
{search === '' || filterV === 'All' ? (
[]
) : filteredResult.length > 10 ? (
<p>Too many searches, specify another filter</p>
) : filterV !== 'All' ||
(filteredResult.length <= 10 && filteredResult.length > 1) ? (
filteredResult.map((country) => (
<CountryName
key={country.cca3}
country={country}
/>
))
) : (
filteredResult.map((country) => (
<Country key={country.cca3} country={country} />
))
)}
All done now! π we can tidy up our code by clearing the console.logs
and testing to make sure the Search feature still works.
I hope this article helps someone get unstuck, and once again pardon all grammatical errors. If there is a better way this can be achieved I would like to learn from you in the comments π
https://fullstackopen.com/en/ is a good resource to learn React. don't stall, start now! βοΈ π