UNPKG

qs-state-hook

Version:
104 lines (100 loc) 3.4 kB
import { useRef, useMemo, useCallback, useState, useEffect } from 'react'; import qs from 'qs'; import debounce from 'lodash.debounce'; const theLocation = typeof window !== "undefined" ? window.location : { search: "" }; const push = ({ search }) => { const url = new URL(theLocation.toString?.() || ""); url.search = search; history.pushState("", "", url.toString()); }; var history$1 = { location: theLocation, push }; function isEqualObj(a, b) { if (a === b) return true; return JSON.stringify(a) === JSON.stringify(b); } const COMMIT_DELAY = 100; let commitQueue = {}; const identityFn = (v) => v; function useQsStateCreator(options = {}) { const { commit: commitToLocation = history$1.push } = options; const location = useRef({ search: "" }); location.current = options.location || history$1.location; const commit = useMemo(() => debounce(() => { const parsedQS = qs.parse(location.current?.search || "", { ignoreQueryPrefix: true }); const qsObject = { ...parsedQS, ...commitQueue }; commitToLocation({ search: qs.stringify(qsObject, { skipNulls: true }) }); commitQueue = {}; }, COMMIT_DELAY), [commitToLocation]); const storeInURL = useCallback((k, v) => { commitQueue = { ...commitQueue, [k]: v }; commit(); }, [commit]); return useMemo(() => qsStateFactory({ storeInURL, location: location.current }), [storeInURL]); } function qsStateFactory({ storeInURL, location }) { function useQsState(def) { const mounted = useRef(false); const { hydrator = identityFn, dehydrator = identityFn } = def; const locSearch = location.search; const validator = useCallback((v) => { const userValidator = def.validator; if (Array.isArray(userValidator)) { return userValidator.indexOf(v) !== -1; } else if (typeof userValidator === "function") { return userValidator(v); } else { return !!v; } }, [def.validator]); const getValueFromURL = (searchString) => { const parsedQS = qs.parse(searchString, { ignoreQueryPrefix: true }); const value = hydrator(parsedQS[def.key]); return validator(value) ? value : def.default; }; const [valueState, setValueState] = useState(getValueFromURL(locSearch)); const stateRef = useRef(); stateRef.current = valueState; const setValue = useCallback((value) => { const v = validator(value) ? value : def.default; const dehydratedVal = dehydrator(v); setValueState(v); const dehydratedDefaultVal = dehydrator(def.default); storeInURL(def.key, dehydratedVal !== dehydratedDefaultVal ? dehydratedVal : null); }, [validator, def.default, def.key, dehydrator, storeInURL]); useEffect(() => { if (!mounted.current) { mounted.current = true; return; } const v = getValueFromURL(locSearch); const hasPendingCommits = Object.keys(commitQueue).length; if (!hasPendingCommits && !isEqualObj(v, stateRef.current)) { setValueState(v); } }, [locSearch, def]); return [valueState, setValue]; } useQsState.memo = function useQsStateMemoized(def, deps = []) { return useQsState(useMemo(() => def, [...deps, storeInURL])); }; return useQsState; } export { useQsStateCreator as default }; //# sourceMappingURL=index.mjs.map