diff --git a/frontend/app/components/SearchFilter.tsx b/frontend/app/components/SearchFilter.tsx index c6b7c9ab..c09649d1 100644 --- a/frontend/app/components/SearchFilter.tsx +++ b/frontend/app/components/SearchFilter.tsx @@ -1,5 +1,6 @@ "use client"; +import { useEffect, useRef, useState } from "react"; import { useLanguage } from "@/app/contexts/LanguageContext"; import { t } from "@/lib/ui-translations"; @@ -42,13 +43,34 @@ export default function SearchFilter({ extra, }: SearchFilterProps) { const { lang } = useLanguage(); + + // Decouple input value from upstream `search` state so each keystroke + // doesn't re-trigger the parent's URL update + API fetch (which caused + // the typing flicker on /cards, /relics, /potions, etc. — issue #274). + // Local `draft` advances immediately; `onSearchChange` fires 200ms after + // typing stabilizes. External `search` prop changes (e.g. URL → state + // sync on mount, or a clear-filter button) flow back into the draft. + const [draft, setDraft] = useState(search); + const onSearchChangeRef = useRef(onSearchChange); + useEffect(() => { + onSearchChangeRef.current = onSearchChange; + }); + useEffect(() => { + setDraft(search); + }, [search]); + useEffect(() => { + if (draft === search) return; + const timer = setTimeout(() => onSearchChangeRef.current(draft), 200); + return () => clearTimeout(timer); + }, [draft, search]); + return (