Search and filter data using React Hooks and a headless CMS
Searching and filtering data is a common feature for websites and apps, especially e-commerce. In this article, we will discuss how to build a search and filter feature for products using React. The product data will be queried from a headless CMS (Cosmic) with the UI updated instantly. We also will talk about how state management is handled using Hooks and API usage optimization with the debounce technique.
Install template demo content
To get started with this example, install the uNFT Marketplace template which includes the demo content that we will use for the search and filter functionality. Or you can skip this step and just follow along with the code.
- Log in to your Cosmic account.
- Go to the uNFT Marketplace and click “Select Template”.
- Follow the steps to create a new Project and Bucket and import the demo content.
- See the demo content now installed in your Bucket.
- Go to Products and note the Metafields which contain the data that we will use for our React search / filter feature.
Create the search / filter React app
To make things easier, we’ve already built a small app with the search / filter feature available on StackBlitz.
Using the Cosmic API we can filter the product results by search criteria. Now let’s see how it works.
Create the search / filter query
To filter the products in our feature, we will need to send a request to the Cosmic API to get only the products that match the search criteria. To do this, we will use Cosmic Queries.
To create the query, we create an object query
with properties that match up with the Object Metadata values that we are searching for like color
, price
, and categories
.
Here is an example with comments to explain what each query property does.
const query = {
// Find Objects in products Object Type
"type":"products"
// Find products with a price greater than or equal to 10 and less than or equal to 100
"metadata.price":{
"$gte":10,
"$lte":100
},
// Find products that have the color Blue
"metadata.color":"Blue",
// Find products that have the category Sale (uses category Object id)
"metadata.categories":"627e23f18bb13a000911ea55",
}
After we build our query, we send the query to the Cosmic NPM module using the getObjects
method. We use props
to limit the response to only the properties we need. Here's an example of what the implementation looks like.
import Cosmic from 'cosmicjs';
const bucket = Cosmic().bucket({
slug: "YOUR_COSMIC_BUCKET_SLUG",
read_key: "YOUR_COSMIC_BUCKET_READ_KEY",
});
const params = {
query,
props: 'title,slug,metadata,created_at'
}
const data = await bucket.getObjects(params);
Now let’s get into the details about how React handles the UI state updates using Hooks.
React Hooks
React uses one-way data flow, passing data down the component hierarchy from parent to child components and Hooks allow function components to have access to state. For every search and filter update to the input fields, we are adding state to the React application using the state hook useState
.
// App.js
const [search, setSearch] = useState('');
const [{ min, max }, setRangeValues] = useState({ min: '', max: '' });
To display the filtered data on the webpage, we map over the filterResult
array and display the appropriate list of products.
// App.js
<div className={styles.list}>
{filterResult?.length ? (
filterResult?.map(product => (
<Card className={styles.card} item={product} key={product.slug} />
))
) : (
<p className={styles.inform}>Try another category!</p>
)}
</div>
React Custom Hook useDebounce
When the user types something in the input field, the state of the search
variable will be updated. To improve the search and filter experience we will create a React Custom Hook useDebounce
.
This hook enables a debounce that clears any fast changing value. The debounced value will only reflect the latest value when the useDebounce
hook has not been called for the specified time period. When used in conjunction with useEffect
, you can ensure that expensive operations like API calls are not executed too frequently.
// utils/hooks/useDebounce.js
import { useState, useEffect } from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
// Update debounced value after delay
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cancel the timeout if value changes (also on delay change or unmount)
return () => {
clearTimeout(handler);
};
}, [value, delay]); // Only re-call effect if value or delay changes
return debouncedValue;
}
export default useDebounce;
The example below allows you to search the Cosmic API and uses useDebounce
to prevent API calls from being fired on every keystroke. The goal is to only have the API call fire when the user stops typing, so that we aren't hitting the Cosmic API rapidly.
// App.js
const [search, setSearch] = useState('');
// Debounce search term so that it only gives us latest value
// If search has not been updated within last 500ms, query the Cosmic API
const debouncedSearchTerm = useDebounce(search, 500);
useEffect(() => {
let isMount = true;
if (isMount && debouncedSearchTerm?.length) {
handleFilterDataByParams({
search: debouncedSearchTerm,
});
}
return () => {
isMount = false;
};
}, [debouncedSearchTerm]);
Conclusion
Searching and filtering is a common feature for many websites and apps which can prove challenging to build. But by using the right tools and techniques, building this feature can be made easier and more enjoyable for the end user.
In this article, we showed you how you can use Cosmic Queries, React Hooks, and debounce to create a great search and filter experience. The examples explained in this article are all part of the uNFT Marketplace app template. You can view the full demo here, install the app template in your Cosmic dashboard, or clone the GitHub repository. To learn more about Cosmic Queries go to the Cosmic documentation.
If you have any questions about Cosmic integration and features, you can reach out to them on Twitter, Slack or follow YouTube channel.