diff --git a/extensions/react-widget/src/components/SearchBar.tsx b/extensions/react-widget/src/components/SearchBar.tsx index f4e55a58..8cf30632 100644 --- a/extensions/react-widget/src/components/SearchBar.tsx +++ b/extensions/react-widget/src/components/SearchBar.tsx @@ -36,7 +36,6 @@ const themes = { const Main = styled.div` all:initial; - font-family: sans-serif; ` const TextField = styled.input<{ inputWidth: string }>` @@ -206,12 +205,14 @@ export const SearchBar = ({ width = "240px" }: SearchBarProps) => { const [input, setInput] = React.useState(""); - const [loading, setLoading] = React.useState(false) + const [loading, setLoading] = React.useState(false); const [isWidgetOpen, setIsWidgetOpen] = React.useState(false); const inputRef = React.useRef(null); const resultsRef = React.useRef(null); const [isResultVisible, setIsResultVisible] = React.useState(true); const [results, setResults] = React.useState([]); + const debounceTimeout = React.useRef(null); + const abortControllerRef = React.useRef(null) React.useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if ( @@ -228,18 +229,32 @@ export const SearchBar = ({ }; }, []) React.useEffect(() => { - input.length > 0 ? - (() => { - setLoading(true) - getSearchResults(input, apiKey, apiHost) - .then((data) => { - setResults(data) - setLoading(false) - }) + if (!input) { + setResults([]); + return; + } + if (debounceTimeout.current) { + clearTimeout(debounceTimeout.current); + } + + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + const abortController = new AbortController(); + abortControllerRef.current = abortController; + + debounceTimeout.current = setTimeout(() => { + setLoading(true); + getSearchResults(input, apiKey, apiHost, abortController.signal) + .then((data) => setResults(data)) .catch((err) => console.log(err)) - })() - : - setResults([]) + .finally(() => setLoading(false)); + }, 500); + + return () => { + abortController.abort(); + clearTimeout(debounceTimeout.current ?? undefined); + }; }, [input]) const handleKeyDown = (event: React.KeyboardEvent) => { diff --git a/extensions/react-widget/src/requests/searchAPI.ts b/extensions/react-widget/src/requests/searchAPI.ts index 18df3691..7411a810 100644 --- a/extensions/react-widget/src/requests/searchAPI.ts +++ b/extensions/react-widget/src/requests/searchAPI.ts @@ -1,34 +1,37 @@ import { Result } from "@/types"; - async function getSearchResults(question: string, apiKey:string, apiHost:string): Promise { - - const payload = { - question, - api_key:apiKey - }; - - try { - const response = await fetch(`${apiHost}/api/search`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(payload), - }); - - if (!response.ok) { - throw new Error(`Error: ${response.status}`); - } - - const data: Result[] = await response.json(); - return data; - - } catch (error) { - console.error("Failed to fetch documents:", error); - throw error; +async function getSearchResults(question: string, apiKey: string, apiHost: string, signal: AbortSignal): Promise { + + const payload = { + question, + api_key: apiKey + }; + + try { + const response = await fetch(`${apiHost}/api/search`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + signal: signal + }); + + if (!response.ok) { + throw new Error(`Error: ${response.status}`); } + + const data: Result[] = await response.json(); + return data; + + } catch (error) { + if (!(error instanceof DOMException && error.name == "AbortError")) { + console.error("Failed to fetch documents:", error); + } + throw error; } - - export { - getSearchResults - } \ No newline at end of file +} + +export { + getSearchResults +} \ No newline at end of file