Search and filter data using React Hooks and a headless CMS
Tags: react, javascript, webdev, tutorial
Published: 2022-08-04 15:39:00

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](https://www.cosmicjs.com/headless-cms) ([Cosmic](https://www.cosmicjs.com/)) with the UI updated instantly. We will also talk about how the debounce technique handles state management using Hooks and API usage optimization.

### Install template demo content

To get started with this example, install the [uNFT Marketplace](https://www.cosmicjs.com/apps/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.

1. Log in to your [Cosmic account](https://app.cosmicjs.com/login).
2. Go to the [uNFT Marketplace](https://www.cosmicjs.com/apps/unft-marketplace) and click “Select Template”.
3. Follow the steps to create a new Project and Bucket and import the demo content.
4. See the demo content now installed in your Bucket.
5. Go to Products and note the Metafields which contain the data that we will use for our React search / filter feature.

![uNFT](https://imgix.cosmicjs.com/d2327370-0195-11ed-b7be-d956591ad437-home-dark.gif?w=1000)

## Create the search / filter React app

To make things easier, we’ve already built a small app with the search / filter feature available on <a href="https://stackblitz.com/edit/react-search-filter-cms?file=src%2FApp.js" target="_blank">StackBlitz</a>.

<a href="https://stackblitz.com/edit/react-search-filter-cms?file=src%2FApp.js" target="_blank"><img src="https://imgix.cosmicjs.com/a7e513f0-1376-11ed-b476-13ceb56f12f2-stackblitz.png?w=1200&formt=auto" alt="StackBlitz"/></a>

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. We will use [Cosmic Queries](https://docs.cosmicjs.com/api-reference/queries) to do this. 

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`.

![https://imgix.cosmicjs.com/023df770-136a-11ed-b476-13ceb56f12f2-Cosmic-Bucket.png](https://imgix.cosmicjs.com/023df770-136a-11ed-b476-13ceb56f12f2-Cosmic-Bucket.png)

Here is an example with comments to explain what each query property does.

```jsx
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](https://www.npmjs.com/package/cosmicjs) 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.

```jsx
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`.

```jsx
// 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.

```jsx
// 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>
```

![https://imgix.cosmicjs.com/1d1cef10-136a-11ed-b476-13ceb56f12f2-React-search-and-filter-Cosmic.gif](https://imgix.cosmicjs.com/1d1cef10-136a-11ed-b476-13ceb56f12f2-React-search-and-filter-Cosmic.gif)

## 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](https://www.w3schools.com/react/react_customhooks.asp) `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.

```jsx
// 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 only to have the API call fire when the user stops typing, so we aren't hitting the Cosmic API rapidly.

```jsx
// 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](https://www.cosmicjs.com/articles/build-a-digital-art-marketplace-with-nextjs-cosmic-and-stripe). You can view the full demo [here](https://unft-marketplace-cosmicjs.vercel.app/), install the [app template](https://www.cosmicjs.com/apps/unft-marketplace) in your Cosmic dashboard, or clone the [GitHub repository](https://github.com/cosmicjs/unft-marketplace).  To learn more about Cosmic Queries go to the [Cosmic documentation](https://docs.cosmicjs.com/api-reference/queries).

If you have any questions about Cosmic integration and features, you can reach out to them on [Twitter](https://twitter.com/cosmicjs), [Slack](https://cosmicslack.herokuapp.com/) or follow to  [YouTube channel](https://www.youtube.com/cosmicjs).