diff --git a/src/DocSearchModal.tsx b/src/DocSearchModal.tsx index 9d511874..70d90e3a 100644 --- a/src/DocSearchModal.tsx +++ b/src/DocSearchModal.tsx @@ -102,6 +102,14 @@ export const DocSearchModal: Component = ({ ); const numberOfHits = () => hits().length; + // AbortController for cancelling previous search requests + let abortController: AbortController | null = null; + + // Cancel ongoing requests when component unmounts + onCleanup(() => { + abortController?.abort(); + }); + function onKeyDown( e: KeyboardEvent & { currentTarget: HTMLInputElement; @@ -167,6 +175,9 @@ export const DocSearchModal: Component = ({ } function onReset() { + // Cancel any ongoing search request + abortController?.abort(); + abortController = null; setLoading(false); setScreenState(ScreenState.EmptyQuery); setHits([]); @@ -175,30 +186,61 @@ export const DocSearchModal: Component = ({ } function search(query: string) { + // Cancel previous search request if it exists + if (abortController) { + abortController.abort(); + } + + // Create new AbortController for this search + abortController = new AbortController(); + const currentController = abortController; + setLoading(true); searchClient() .index(indexUid) - .search(query, { - attributesToHighlight: ["*"], - attributesToCrop: [`content`], - cropLength: 30, - ...searchParams, - }) + .search( + query, + { + attributesToHighlight: ["*"], + attributesToCrop: [`content`], + cropLength: 30, + ...searchParams, + }, + { + signal: abortController.signal, + }, + ) .catch((e) => { - onReset(); + // Don't show error if request was aborted (user is still typing) + if (e.name === "AbortError") { + return; + } + // Check if this is still the current request before showing error + if (currentController.signal.aborted) { + return; + } + // Don't call onReset() as it would abort the current (possibly new) request + setLoading(false); setScreenState(ScreenState.Error); + setHits([]); + setHitsCategories([]); console.error(e); }) .then((res) => { + // Check if this request was cancelled + if (currentController.signal.aborted) { + return; + } + if (!res) { - onReset(); - setScreenState(ScreenState.Error); return; } if (res.hits.length === 0) { - onReset(); + setLoading(false); setScreenState(ScreenState.NoResults); + setHits([]); + setHitsCategories([]); return; }