UNPKG

use-searchable-list

Version:

A hook to filter an array/list of objects.

101 lines (87 loc) 2.63 kB
import { useState, useEffect, useMemo } from 'react'; import { SearchableListItem, SearchableListProps, UseSearchableListHook, Primitive } from './@types'; const defaults: SearchableListProps = { clearOnEmpty: false, firstLetterCheck: true, debounce: true, delay: 300 }; const useSearchableList = <T extends SearchableListItem>( property: keyof T, props: SearchableListProps = defaults ): UseSearchableListHook<T> => { const { clearOnEmpty, firstLetterCheck, debounce, delay } = useMemo( () => ({ ...defaults, ...props }), [props] ); if (!isPrimitive(property)) { throw new Error('Invalid property used to filter. Only primitive types are allowed.'); } const [origin, setOrigin] = useState<T[]>([]); const [state, setState] = useState<T[]>([]); const [searchTerm, setSearchTerm] = useState<string>(''); const [initialized, setInitialized] = useState<boolean>(false); const setValue = (value: T[]): void => { setOrigin(value); setState(value); }; const filter = (value: Primitive): void => { setSearchTerm(value.toString()); }; useEffect(() => { if (initialized) { if (debounce) { const delayTimeout = setTimeout(() => { applyFilter(); }, delay); return () => clearTimeout(delayTimeout); } else { applyFilter(); } } else { setState(origin); setInitialized(true); } }, [searchTerm]); const applyFilter = (): void => { if (searchTerm === '' || searchTerm === undefined) { reset(); } else if (searchTerm.length === 1 && firstLetterCheck) { const filteredList = origin.filter((element: T) => { const propertyValue = element[property]; return ( isPrimitive(propertyValue) && propertyValue.toString().toLowerCase().startsWith(searchTerm.toLowerCase()) ); }); setState(filteredList); } else { const filteredList = origin.filter((element: T) => { const propertyValue = element[property]; return ( isPrimitive(propertyValue) && propertyValue.toString().toLowerCase().includes(searchTerm.toLowerCase()) ); }); setState(filteredList); } }; const reset = (): void => { if (clearOnEmpty) { setState([]); } else { setState(origin); } }; return [state, setValue, filter]; }; const isPrimitive = (value: unknown): value is Primitive => { return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'; }; export default useSearchableList; export * from './@types/index';