@baurine/use-url-state
Version:
react useUrlState hook
68 lines (65 loc) • 2.19 kB
JavaScript
import { jsx } from 'react/jsx-runtime';
import { createContext, useState, useMemo, useContext } from 'react';
const UrlStateContext = createContext(null);
const useUrlStateContext = () => {
const context = useContext(UrlStateContext);
if (!context) {
throw new Error('useUrlStateContext must be used within a UrlStateProvider');
}
return context;
};
function defCtxVal() {
return {
urlQuery: new URL(window.location.href).search,
setUrlQuery(p) {
const url = new URL(window.location.href);
window.history.replaceState({}, '', `${url.pathname}?${p}`);
}
};
}
function UrlStateProvider(props) {
const val = props.value || defCtxVal();
const [urlQuery, _setUrlQuery] = useState(val.urlQuery);
// UrlStateProvider is designed to each page has its own provider instance,
// won't share between pages
// so we don't need to sync urlQuery from props
// -------------------
// sync urlQuery from props changes
// useEffect(() => {
// _setUrlQuery(val.urlQuery)
// }, [val.urlQuery])
const ctxValue = useMemo(() => ({
urlQuery,
setUrlQuery: (v) => {
val.setUrlQuery(v);
// trigger re-render
_setUrlQuery(v);
}
}), [urlQuery, val]);
return (jsx(UrlStateContext.Provider, { value: ctxValue, children: props.children }));
}
function useUrlState() {
const { urlQuery, setUrlQuery } = useUrlStateContext();
const queryParams = useMemo(() => {
const searchParams = new URLSearchParams(urlQuery);
const paramsObj = {};
searchParams.forEach((v, k) => {
paramsObj[k] = v;
});
return paramsObj;
}, [urlQuery]);
function setQueryParams(s) {
const searchParams = new URLSearchParams(urlQuery);
Object.keys(s).forEach((k) => {
if (s[k]) {
searchParams.set(k, s[k]);
}
else {
searchParams.delete(k);
}
});
setUrlQuery(searchParams.toString());
}
return [queryParams, setQueryParams];
}
export { UrlStateProvider, useUrlState };