import { useCallback, useState } from "react"; type IdentifyFn = (item: T, val: V) => boolean; type RetrieveFn = (val: V) => T; const defaultIdentify: IdentifyFn = (item, value) => item === value; const defaultRetrieve: RetrieveFn = (value) => value; const addTo = (state: T[], value: V, identify: IdentifyFn, retrieve: RetrieveFn) => { if(state.find(item => identify(item, value))){ return state; } else { return [...state, retrieve(value)]; } }; const removeFrom = (state: T[], value: V, identify: IdentifyFn) => { return state.filter(item => !identify(item, value)); }; type UseMultiArgs = Partial<{ start: T[], identify: IdentifyFn, retrieve: RetrieveFn }>; /** * Custom hook to work with component state that is based on a multiple selection */ const useMulti = ({ start = [], identify = defaultIdentify, retrieve = defaultRetrieve }: UseMultiArgs) => { const [state, setState] = useState(start); const add = useCallback((val: V) => { setState(prevState => addTo(prevState, val, identify, retrieve)); }, [identify, retrieve]); const remove = useCallback((val: V) => { setState(prevState => removeFrom(prevState, val, identify)); }, [identify]); const toggle = useCallback((val: V) => { setState(prevState => { if(prevState.find(item => identify(item, val))){ return removeFrom(prevState, val, identify); } else { return addTo(prevState, val, identify, retrieve); } }); }, [identify, retrieve]); const set = useCallback((value: T[]) => { setState(value); }, []); return [state, { set, toggle, add, remove }] as const; }; export { useMulti };