DEV Community

Cover image for [React, Next.js] Search function using useDebounce
Kota Ito
Kota Ito

Posted on

[React, Next.js] Search function using useDebounce

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

 const [searchText, setSearchText] = React.useState('');
  const [searchTextQuery] = useDebounce(searchText, 700);
Enter fullscreen mode Exit fullscreen mode

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.

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

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.

 const {
    data: piecesData,
    loading: piecesLoading,
    error: piecesError,
  } = useQuery(SEARCH_PIECESS_QUERY, {
    variables: {
      userId,
      searchText: searchTextQuery,
    },
    skip: !searchTextQuery,
  });
Enter fullscreen mode Exit fullscreen mode

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

React.useEffect(() => {
    if (piecesData && !piecesError && !piecesLoading) {
      setPieceSearchResult(piecesData.pieces_search);
    }
  }, [
    searchTextQuery,
    piecesData,
    piecesError,
    piecesLoading,
  ]);
Enter fullscreen mode Exit fullscreen mode

This will update the search result display section UI below

<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>
Enter fullscreen mode Exit fullscreen mode

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.

<ModalComponent>
  <input/>
  <searchResultDisplaySection/>
</ModalComponent>
Enter fullscreen mode Exit fullscreen mode

Cover image:
from Unsplash
taken by Kenny Eliason

Top comments (0)