Search code examples
reactjslodashdebouncing

Lodash debounce is not working in react js


I'm trying to search for a product from my database and using lodash's debounce to send request after a certain time. The problem I'm facing is that the request is going after the specified time only but requests are fired for each character after the time interval specified. I want only one request to be triggered and that to for the entire word, not for each letter. What am i doing wrong here ? Also, I'm open to any optimization in this code if any :)

SearchBar.js

function SearchBar() {
    const [searchText, setSearchText] = useState("");
    const searchResultsRef = useRef(null);
    const dispatch = useDispatch()

    const sendRequest = (searchText) => {
        dispatch(getSearchedCategories(searchText))
    };
      
    const debouncedSendRequest = debounce(sendRequest, 5000);
      
    const handleInputChange = (event) => {
        const inputValue = event.target.value.toLowerCase();
        if (inputValue === "") {
            setSearchText("");
        } else {
            setSearchText(inputValue);
            if(inputValue.trim().length > 2) {
                debouncedSendRequest(inputValue.trim())
                setShowSearchResult(true)
            }
        }
    };

    const hideSearchResults = () => {
        setShowSearchResult(false);
    }

    useEffect(() => {
        const handleClickOutside = (event) => {
            if (searchResultsRef.current && !searchResultsRef.current.contains(event.target)) {
                hideSearchResults();
            }
        };
        // add event listener to detect clicks outside of the search component
        document.addEventListener('mousedown', handleClickOutside);
        return () => {
            document.removeEventListener('mousedown', handleClickOutside);
        };
    }, [searchResultsRef]);

    return (
        <div className="searchContainer">
            <div className="searchInputs">
                <input
                    type="search"
                    placeholder="Search for a product"
                    value={searchText}
                    onChange={handleInputChange}
                />
                ...
            </div>
            
            ...
        </div>
    );
}

action.js

export const getSearchItem = (searchedItem) => async (dispatch) => {
    try {
        ...
        const { data } = axios.get(// my api)
        ...
    } catch(error) {
        ...
    }
}

Solution

  • const debouncedSendRequest = debounce(sendRequest, 5000);
    

    Every time the component renders, this line creates a brand new debouncedSendRequest function. It has its own internal timer not related to the last time you created it. For the debouncing to work you must create the function just once. You can do this with useMemo:

    const debouncedSendRequest = useMemo(() => {
      const sendRequest = (searchText) => {
        dispatch(getSearchedCategories(searchText))
      };
          
      return debounce(sendRequest, 5000);
    }, [dispatch]);