Files
cinny/src/app/hooks/useAsyncSearch.ts
T

86 lines
2.4 KiB
TypeScript
Raw Normal View History

2023-06-12 21:15:23 +10:00
import { useCallback, useEffect, useMemo, useState } from 'react';
import {
MatchHandler,
AsyncSearch,
AsyncSearchHandler,
AsyncSearchOption,
MatchQueryOption,
NormalizeOption,
normalize,
matchQuery,
ResultHandler,
} from '../utils/AsyncSearch';
export type UseAsyncSearchOptions = AsyncSearchOption & {
matchOptions?: MatchQueryOption;
normalizeOptions?: NormalizeOption;
};
export type SearchItemStrGetter<TSearchItem extends object | string | number> = (
2023-10-19 17:43:16 +11:00
searchItem: TSearchItem,
query: string
2023-06-12 21:15:23 +10:00
) => string | string[];
export type UseAsyncSearchResult<TSearchItem extends object | string | number> = {
query: string;
items: TSearchItem[];
};
2023-06-22 09:14:50 +10:00
export type SearchResetHandler = () => void;
2023-06-12 21:15:23 +10:00
export const useAsyncSearch = <TSearchItem extends object | string | number>(
list: TSearchItem[],
getItemStr: SearchItemStrGetter<TSearchItem>,
options?: UseAsyncSearchOptions
2023-06-22 09:14:50 +10:00
): [UseAsyncSearchResult<TSearchItem> | undefined, AsyncSearchHandler, SearchResetHandler] => {
2023-06-12 21:15:23 +10:00
const [result, setResult] = useState<UseAsyncSearchResult<TSearchItem>>();
const [searchCallback, terminateSearch] = useMemo(() => {
setResult(undefined);
const handleMatch: MatchHandler<TSearchItem> = (item, query) => {
2023-10-19 17:43:16 +11:00
const itemStr = getItemStr(item, query);
2023-06-12 21:15:23 +10:00
if (Array.isArray(itemStr))
return !!itemStr.find((i) =>
matchQuery(normalize(i, options?.normalizeOptions), query, options?.matchOptions)
);
return matchQuery(
normalize(itemStr, options?.normalizeOptions),
query,
options?.matchOptions
);
};
const handleResult: ResultHandler<TSearchItem> = (results, query) =>
setResult({
query,
2023-06-22 09:14:50 +10:00
items: [...results],
2023-06-12 21:15:23 +10:00
});
return AsyncSearch(list, handleMatch, handleResult, options);
}, [list, options, getItemStr]);
const searchHandler: AsyncSearchHandler = useCallback(
(query) => {
const normalizedQuery = normalize(query, options?.normalizeOptions);
searchCallback(normalizedQuery);
},
[searchCallback, options?.normalizeOptions]
);
2023-06-22 09:14:50 +10:00
const resetHandler: SearchResetHandler = useCallback(() => {
terminateSearch();
setResult(undefined);
}, [terminateSearch]);
2023-06-12 21:15:23 +10:00
useEffect(
() => () => {
// terminate any ongoing search request on unmount.
terminateSearch();
},
[terminateSearch]
);
2023-06-22 09:14:50 +10:00
return [result, searchHandler, resetHandler];
2023-06-12 21:15:23 +10:00
};