Skip to content

Commit ec3e1b7

Browse files
committed
fix: debounce URL navigation in skills search to reduce input lag
onQueryChange called navigate() on every keystroke to sync the query to the URL. Each navigate triggers history.replaceState, TanStack Router re-evaluation, and useSearch() invalidation, causing multiple re-renders per keystroke. Keep setQuery() immediate so the controlled input stays responsive, but debounce the navigate() call at 220 ms (matching the existing search-action debounce). Cancel the pending timer when search.q changes externally (browser back/forward) to prevent the debounced navigate from overwriting the external URL change. Made-with: Cursor
1 parent bc06dbf commit ec3e1b7

1 file changed

Lines changed: 14 additions & 5 deletions

File tree

src/routes/skills/-useSkillsBrowseModel.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export function useSkillsBrowseModel({
3939
const searchRequest = useRef(0)
4040
const loadMoreRef = useRef<HTMLDivElement | null>(null)
4141
const loadMoreInFlightRef = useRef(false)
42+
const navigateTimer = useRef<number>(0)
4243

4344
const view: SkillsView = search.view ?? 'list'
4445
const highlightedOnly = search.highlighted ?? false
@@ -74,6 +75,7 @@ export function useSkillsBrowseModel({
7475
const isLoadingMoreList = paginationStatus === 'LoadingMore'
7576

7677
useEffect(() => {
78+
window.clearTimeout(navigateTimer.current)
7779
setQuery(search.q ?? '')
7880
}, [search.q])
7981

@@ -216,14 +218,21 @@ export function useSkillsBrowseModel({
216218
return () => observer.disconnect()
217219
}, [canLoadMore, loadMore])
218220

221+
useEffect(() => {
222+
return () => window.clearTimeout(navigateTimer.current)
223+
}, [])
224+
219225
const onQueryChange = useCallback(
220226
(next: string) => {
221-
const trimmed = next.trim()
222227
setQuery(next)
223-
void navigate({
224-
search: (prev) => ({ ...prev, q: trimmed ? next : undefined }),
225-
replace: true,
226-
})
228+
window.clearTimeout(navigateTimer.current)
229+
const trimmed = next.trim()
230+
navigateTimer.current = window.setTimeout(() => {
231+
void navigate({
232+
search: (prev) => ({ ...prev, q: trimmed ? next : undefined }),
233+
replace: true,
234+
})
235+
}, 220)
227236
},
228237
[navigate],
229238
)

0 commit comments

Comments
 (0)