qs-state-hook
Version:
URL querystring state manager
111 lines (104 loc) • 3.76 kB
JavaScript
;
var react = require('react');
var qs = require('qs');
var debounce = require('lodash.debounce');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var qs__default = /*#__PURE__*/_interopDefaultLegacy(qs);
var debounce__default = /*#__PURE__*/_interopDefaultLegacy(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 = react.useRef({ search: "" });
location.current = options.location || history$1.location;
const commit = react.useMemo(() => debounce__default["default"](() => {
const parsedQS = qs__default["default"].parse(location.current?.search || "", {
ignoreQueryPrefix: true
});
const qsObject = {
...parsedQS,
...commitQueue
};
commitToLocation({
search: qs__default["default"].stringify(qsObject, { skipNulls: true })
});
commitQueue = {};
}, COMMIT_DELAY), [commitToLocation]);
const storeInURL = react.useCallback((k, v) => {
commitQueue = {
...commitQueue,
[k]: v
};
commit();
}, [commit]);
return react.useMemo(() => qsStateFactory({ storeInURL, location: location.current }), [storeInURL]);
}
function qsStateFactory({ storeInURL, location }) {
function useQsState(def) {
const mounted = react.useRef(false);
const {
hydrator = identityFn,
dehydrator = identityFn
} = def;
const locSearch = location.search;
const validator = react.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__default["default"].parse(searchString, { ignoreQueryPrefix: true });
const value = hydrator(parsedQS[def.key]);
return validator(value) ? value : def.default;
};
const [valueState, setValueState] = react.useState(getValueFromURL(locSearch));
const stateRef = react.useRef();
stateRef.current = valueState;
const setValue = react.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]);
react.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(react.useMemo(() => def, [...deps, storeInURL]));
};
return useQsState;
}
module.exports = useQsStateCreator;
//# sourceMappingURL=index.js.map