buster/web/src/hooks/useDebounceSearch.ts

61 lines
1.5 KiB
TypeScript
Raw Normal View History

2025-01-11 06:54:39 +08:00
import { useDebounceFn, useMemoizedFn } from 'ahooks';
import { useEffect, useState, useTransition } from 'react';
2025-01-21 06:11:42 +08:00
import isEqual from 'lodash/isEqual';
2025-01-11 06:54:39 +08:00
interface UseDebounceSearchProps<T> {
items: T[];
searchPredicate: (item: T, searchText: string) => boolean;
debounceTime?: number;
2025-01-11 06:54:39 +08:00
}
export const useDebounceSearch = <T>({
items,
searchPredicate,
debounceTime = 150
}: UseDebounceSearchProps<T>) => {
2025-01-11 06:54:39 +08:00
const [isPending, startTransition] = useTransition();
const [searchText, setSearchText] = useState('');
const [filteredItems, setFilteredItems] = useState<T[]>(items);
const filterItems = useMemoizedFn((text: string): T[] => {
if (!text) return items;
const lowerCaseSearchText = text.toLowerCase();
return items.filter((item) => searchPredicate(item, lowerCaseSearchText));
});
const updateFilteredItems = useMemoizedFn((text: string) => {
startTransition(() => {
setFilteredItems(filterItems(text));
});
});
const { run: debouncedSearch } = useDebounceFn(
(text: string) => {
updateFilteredItems(text);
},
{ wait: debounceTime }
2025-01-11 06:54:39 +08:00
);
const handleSearchChange = useMemoizedFn((text: string) => {
setSearchText(text);
if (!text) {
updateFilteredItems(text);
} else {
debouncedSearch(text);
}
2025-01-11 06:54:39 +08:00
});
useEffect(() => {
2025-01-21 06:11:42 +08:00
if (!isEqual(items, filteredItems)) {
setFilteredItems(items);
}
2025-01-11 06:54:39 +08:00
}, [items]);
return {
filteredItems,
searchText,
handleSearchChange,
isPending
};
};