Generic React hook for managing debounced async search state, including loading, error handling, and result mapping. ## Key Components ### `UseSearchConfig` Configuration interface for the hook: - `searchFn` — async function that receives the query string and returns raw results - `mapResult` — transforms each raw result into a `SearchResult` - `debounceMs` — debounce delay in milliseconds (default: `300`) - `minQueryLength` — minimum characters before triggering a search (default: `2`) ### `UseSearchReturn` Return shape exposing: - `query` / `setQuery` — controlled input state - `results` — mapped `SearchResult[]` - `isLoading` — `true` while the async search is in flight - `error` — error message string or `null` - `clearResults` — resets results and error state ### `useSearch(config)` Core hook that orchestrates debouncing via `useDebounce`, fires `searchFn` when the debounced query meets `minQueryLength`, and cancels stale in-flight requests via a `cancelled` flag on cleanup. ## Usage Example ```typescript import { useSearch } from "@/hooks/use-search" import type { Device } from "@/types" const { query, setQuery, results, isLoading, error } = useSearch({ searchFn: async (q) => await api.devices.search(q), mapResult: (device) => ({ id: device.id, label: device.hostname, description: device.ipAddress, }), debounceMs: 400, minQueryLength: 3, }) // Bind to an input ``` > Stale request cancellation is handled automatically — if the query changes before a previous search resolves, its result is silently discarded.