digivorefr / vibe-rickandmorty

A modern web application using the Rick and Morty API
https://vibe-rickandmorty.vercel.app
0 stars 0 forks source link

Search debouncer not working properly #1

Closed digivorefr closed 3 weeks ago

digivorefr commented 3 weeks ago

Issue Description

The search debounce functionality is not working properly in the Rick and Morty app. Currently, searches are triggered immediately with each keystroke instead of waiting for the user to finish typing.

Current Behavior

When searching for locations, characters, or episodes, the search query is triggered with every keystroke.

Expected Behavior

The search should only be triggered after the user has stopped typing for at least 500ms.

Root Cause

The current implementation has a critical flaw in the handleSearch function. The setTimeout is being called on every keystroke, but previous timeouts are never cleared, resulting in multiple searches being triggered.

For example, in src/app/characters/page.tsx, src/app/locations/page.tsx, and src/app/episodes/page.tsx:

const handleSearch = (value: string) => {
  setSearchTerm(value);
  setTimeout(() => {
    setDebouncedSearch(value);
  }, 500);
};

Proposed Fix

We need to clear previous timeouts to properly implement debouncing:

const [searchTimeoutId, setSearchTimeoutId] = useState<NodeJS.Timeout | null>(null);

const handleSearch = (value: string) => {
  setSearchTerm(value);
  if (searchTimeoutId) {
    clearTimeout(searchTimeoutId);
  }
  const timeoutId = setTimeout(() => {
    setDebouncedSearch(value);
  }, 500);
  setSearchTimeoutId(timeoutId);
};

Or alternatively, use a dedicated useDebounce hook:

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

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

This issue is affecting user experience as it causes unnecessary API calls and potential rate limiting.

Affected Pages

digivorefr commented 3 weeks ago

Issue Fixed

I've implemented the fix for the search debounce issue using a dedicated useDebounce hook approach. Here's what was done:

  1. Created a new hooks.ts file in the lib directory with a reusable useDebounce hook:
    
    // src/lib/hooks.ts
    import { useState, useEffect } from "react";

export function useDebounce(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay);

return () => {
  clearTimeout(handler);
};

}, [value, delay]);

return debouncedValue; }



2. Updated all three page components to use this hook:
   - Updated `/src/app/characters/page.tsx`
   - Updated `/src/app/locations/page.tsx`
   - Updated `/src/app/episodes/page.tsx`

In each file:
- Removed the manual `setTimeout` implementation
- Imported the new hook
- Replaced `const [debouncedSearch, setDebouncedSearch] = useState("")` with `const debouncedSearch = useDebounce(searchTerm, 500)`
- Simplified the `handleSearch` function to just update `searchTerm`

The search now properly waits for 500ms of user inactivity before triggering the API calls, which should improve user experience and reduce unnecessary API requests.

This implementation is cleaner, more maintainable, and follows React best practices by using a dedicated hook and cleanup function.