'use client'; import { useEffect, useRef, useState } from 'react'; import { fetchSuggestions, SUGGEST_MIN_QUERY, type Suggestion } from '@/lib/suggest'; /* * Long enough that a fast typist does not fire a request per character, short * enough that the list feels attached to the keyboard. */ const DEBOUNCE_MS = 200; export function useSchoolSuggest(query: string, enabled: boolean) { const [suggestions, setSuggestions] = useState([]); const [open, setOpen] = useState(false); const [activeIndex, setActiveIndex] = useState(-1); // Set when the user dismisses the list, so a re-render does not reopen it. const dismissed = useRef(''); useEffect(() => { const q = query.trim(); if (!enabled || q.length < SUGGEST_MIN_QUERY || dismissed.current === q) { setSuggestions([]); setOpen(false); return; } /* * Abort the superseded request on every keystroke. This is correctness, * not economy: without it a slow response for "st" can land after the fast * one for "st marys" and replace a correct list with a stale one. */ const controller = new AbortController(); const timer = setTimeout(async () => { const rows = await fetchSuggestions(q, controller.signal); if (controller.signal.aborted) return; setSuggestions(rows); setActiveIndex(-1); setOpen(rows.length > 0); }, DEBOUNCE_MS); return () => { clearTimeout(timer); controller.abort(); }; }, [query, enabled]); return { suggestions, open, activeIndex, setActiveIndex, close: () => { dismissed.current = query.trim(); setOpen(false); setActiveIndex(-1); }, }; }