|
1 | 1 | import * as React from 'react' |
2 | | -import { useState, useRef, useEffect } from 'react' |
| 2 | +import { useState, useRef, useEffect, useCallback } from 'react' |
3 | 3 | import { |
4 | 4 | Table, |
5 | 5 | List, |
|
9 | 9 | RotateCcw, |
10 | 10 | SlidersHorizontal, |
11 | 11 | } from 'lucide-react' |
12 | | -import { useDebouncedValue } from '@tanstack/react-pacer' |
| 12 | +import { useDebouncedCallback, useDebouncer } from '@tanstack/react-pacer' |
13 | 13 | import { twMerge } from 'tailwind-merge' |
14 | 14 | import type { FeedViewMode } from '~/db/types' |
15 | 15 |
|
@@ -562,28 +562,37 @@ export function FilterSearch({ |
562 | 562 | // Local state for immediate UI updates |
563 | 563 | const [inputValue, setInputValue] = useState(value || '') |
564 | 564 |
|
565 | | - // Debounce the input value |
566 | | - const [debouncedValue] = useDebouncedValue(inputValue, { |
567 | | - wait: debounceMs, |
568 | | - }) |
569 | | - |
570 | | - // Update parent when debounced value changes |
571 | | - React.useEffect(() => { |
572 | | - onChange(debouncedValue) |
573 | | - // eslint-disable-next-line react-hooks/exhaustive-deps |
574 | | - }, [debouncedValue]) |
| 565 | + const { maybeExecute, state: isPending } = useDebouncer( |
| 566 | + onChange, |
| 567 | + { wait: debounceMs }, |
| 568 | + (state) => ({ |
| 569 | + isPending: state.isPending, |
| 570 | + }), |
| 571 | + ) |
575 | 572 |
|
576 | 573 | // Sync local state when value prop changes externally |
577 | 574 | React.useEffect(() => { |
578 | | - setInputValue(value || '') |
579 | | - }, [value]) |
| 575 | + const nextValue = value || '' |
| 576 | + |
| 577 | + setInputValue((currentValue) => { |
| 578 | + // While the user is editing, keep the local draft authoritative. |
| 579 | + if (isPending) { |
| 580 | + return currentValue |
| 581 | + } |
| 582 | + |
| 583 | + return nextValue |
| 584 | + }) |
| 585 | + }, [isPending, value]) |
580 | 586 |
|
581 | 587 | return ( |
582 | 588 | <input |
583 | 589 | type="text" |
584 | 590 | placeholder={placeholder} |
585 | 591 | value={inputValue} |
586 | | - onChange={(e) => setInputValue(e.target.value)} |
| 592 | + onChange={(e) => { |
| 593 | + setInputValue(e.target.value) |
| 594 | + maybeExecute(e.target.value) |
| 595 | + }} |
587 | 596 | className={twMerge( |
588 | 597 | 'border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-black/40 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500', |
589 | 598 | size === 'sm' ? 'px-2 py-1 text-xs' : 'px-2 py-1 text-sm', |
|
0 commit comments