skip to Main Content

I am trying to create a debounce search and initially when the field is empty the component renders after the provided setTimeout delay. But if I continue to search with the existing keyword it re-renders the List component on each key stroke. How to avoid that?

import { useEffect, useState } from 'react';
import useDebounce from './hooks/useDebounce';
import List from './components/List';

const App: React.FC = () => {
    const [todo, setTodo] = useState<string>("");
    const [query, setQuery] = useState<string | null>("");
    let deBounceSearch = useDebounce(query, 2000);
    useEffect(() => {
        if (deBounceSearch) {
            console.log('Searching...');
        } else {
            console.log('...');
        }       
    }, [deBounceSearch]);

    return (
        <div className="App">   
            <input type="text" placeholder='Search anything' onChange={(e) => setQuery(e.target.value)} />
            {deBounceSearch !== '' && (
                <List />
            )}
        </div>
    );
}

useDebounce.tsx

const useDebounce = (value: any, delay: number) => {
    const [debouncedValue, setDebouncedValue] = useState(value);

    useEffect(() => {
        const handler = setTimeout(() => { setDebouncedValue(value) }, delay);
        return () => {
            clearTimeout(handler);
        }
    }, [value, delay]);

    return debouncedValue;
}    
export default useDebounce;

2

Answers


  1. You can use useMemo to avoid re-render the List component every time query value changes:

    const App: React.FC = () => {
        const [todo, setTodo] = useState<string>("");
        const [query, setQuery] = useState<string | null>("");
        const deBounceSearch = useDebounce(query, 2000);
        // -> 
        const cachedList = React.useMemo(() => <List />, [debouncedValue]);
    
        ...
    
        return (
            <div className="App">   
                <input type="text" placeholder='Search anything' onChange={(e) => setQuery(e.target.value)} />
                {deBounceSearch !== '' && cachedList}
            </div>
        );
    }
    
    

    You also can take a look at React.memo

    Login or Signup to reply.
  2. hello man this is not the best to use debounce i suggest u try lodash debounce with useMemo.

    but for now the solution for your code is that you forgot to clear the timeout on every time the value change.
    here the solution:

    import { useEffect, useState, useRef } from "react";
    import "./styles.css";
    
    const useDebounce = (value, delay) => {
      const [debouncedValue, setDebouncedValue] = useState(value);
      const handler = useRef();
    
      useEffect(() => {
        if (handler.current) {
          clearTimeout(handler.current);
        }
        handler.current = setTimeout(() => {
          setDebouncedValue(value);
        }, delay);
        return () => {
          clearTimeout(handler.current);
        };
      }, [value, delay]);
    
      return debouncedValue;
    };
    
    export default function App() {
      const [value, setValue] = useState("");
      const debounceSearch = useDebounce(value, 2000);
    
      console.log(debounceSearch);
      return (
        <div className="App">
          <input value={value} onChange={(e) => setValue(e.target.value)} />
          <h2>Start editing to see some magic happen!</h2>
        </div>
      );
    }
    

    hope this help .

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search