[React, Next.js] Search function using useDebounce
Tags: react, nextjs, javascript, hooks
Published: 2024-02-29 01:50:43


  

I wanted to create a search function in my app.
When it comes to search function, I personally like the search bar that I don't need to click a search button after I finish typing.
In order to implement the search function, where search result will be displayed automatically on the app UI after user has finished typing, I learned how to use 'useDebounce' custom hook.

## useDebounce?
It has a simple function that returns the value after arbitrary duration of time.

(install useBounce library.
https://www.npmjs.com/package/use-debounce)

## set up state and useDebounce

The useDebounce hook takes two arguments: the value to debounce and the debounce delay time in milliseconds (700ms in this case below).

```javascript
 const [searchText, setSearchText] = React.useState('');
  const [searchTextQuery] = useDebounce(searchText, 700);
```

## Search input

The search bar input is like this below.
So on every keystroke, searchText state changes but searchTextQuery value won't get updated until user has stopped typing for 700ms.

```javascript
 <input
   value={searchText}
   onChange={(e) => setSearchText(e.target.value)}
   placeholder="search..."
   type="text"
   className="w-[250px] h-[30px] mr-sm p-sm rounded-md"
  />
```

## Query using bounced searchText value

Since I'm using GraphQl, it takes an argument called skip where I put searchTextQuery, meaning as long as searchTextQuery is empty, it won't fetch data from the database.

```javascript
 const {
    data: piecesData,
    loading: piecesLoading,
    error: piecesError,
  } = useQuery(SEARCH_PIECESS_QUERY, {
    variables: {
      userId,
      searchText: searchTextQuery,
    },
    skip: !searchTextQuery,
  });
```

## useEffect to set PieceSearchResult state

Finally I set up the useEffect.
After searchTextQuery is returned from useDebounce, if there is fetched piecesData, it will update the pieceSearchResult state 

```javascript
React.useEffect(() => {
    if (piecesData && !piecesError && !piecesLoading) {
      setPieceSearchResult(piecesData.pieces_search);
    }
  }, [
    searchTextQuery,
    piecesData,
    piecesError,
    piecesLoading,
  ]);
```

This will update the search result display section UI below

```javascript
<div className="grid grid-cols-4 gap-x-4 gap-y-5 w-full">
   {piecesLoading && <Loading size="small"></Loading>}
   {!piecesLoading && pieceSearchResult?.length === 0 && (
    <div className="text-sm h-10">No search result</div>)}
   {pieceSearchResult?.map((piece) => {
    return (
     <div key={piece.id}>
      <Link href={`/piece/${piece.id}`} onClick={handleModalClose}>
       <div className="relative w-[100%] aspect-[2/3] rounded-md overflow-hidden mb-1">
        <Image alt="piece image" src={piece.imageUrl} fill objectFit="cover" />
       </div>
      </Link>
     <div className="text-sm">{piece.title}</div>
    </div>);
    })}
</div>
```

**Important note:**
If you put both search input and search result display section in a modal, make sure you pass them as a children like below, so that the whole Modal component won't get re-rendered.

```javascript
<ModalComponent>
  <input/>
  <searchResultDisplaySection/>
</ModalComponent>
```

Cover image: 
from <a href="https://unsplash.com/ja/%E5%86%99%E7%9C%9F/%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%A9%E3%83%97%E3%82%B9%E6%92%AE%E5%BD%B1%E3%81%A7%E7%A9%BA%E4%B8%AD%E3%81%AB%E3%81%84%E3%82%8B%E3%83%90%E3%83%8B%E3%83%BC%E3%81%AE%E3%82%B3%E3%82%B9%E3%83%81%E3%83%A5%E3%83%BC%E3%83%A0%E3%82%92%E7%9D%80%E3%81%9F%E7%94%B7-IF8B82QBCkM?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
taken by <a href="https://unsplash.com/ja/@neonbrand?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Kenny Eliason</a>