nuqs
Version:
Type-safe search params state manager for React - Like useState, but stored in the URL query string
93 lines (90 loc) • 2.94 kB
JavaScript
import { patchHistory, historyUpdateMarker } from './chunk-TCMXVJZC.js';
import { createAdapterProvider, renderQueryString } from './chunk-5WWTJYGR.js';
import mitt from 'mitt';
import { useCallback, startTransition, useState, useEffect } from 'react';
function createReactRouterBasedAdapter({
adapter,
useNavigate,
useSearchParams
}) {
const emitter = mitt();
function useNuqsReactRouterBasedAdapter() {
const navigate = useNavigate();
const searchParams = useOptimisticSearchParams();
const updateUrl = useCallback(
(search, options) => {
startTransition(() => {
emitter.emit("update", search);
});
const url = new URL(location.href);
url.search = renderQueryString(search);
const updateMethod = options.history === "push" ? history.pushState : history.replaceState;
updateMethod.call(
history,
history.state,
// Maintain the history state
historyUpdateMarker,
url
);
if (options.shallow === false) {
navigate(
{
// Somehow passing the full URL object here strips the search params
// when accessing the request.url in loaders.
hash: url.hash,
search: url.search
},
{
replace: true,
preventScrollReset: true,
state: history.state?.usr
}
);
}
if (options.scroll) {
window.scrollTo(0, 0);
}
},
[navigate]
);
return {
searchParams,
updateUrl
};
}
function useOptimisticSearchParams() {
const [serverSearchParams] = useSearchParams(
// Note: this will only be taken into account the first time the hook is called,
// and cached for subsequent calls, causing problems when mounting components
// after shallow updates have occurred.
typeof location === "undefined" ? new URLSearchParams() : new URLSearchParams(location.search)
);
const [searchParams, setSearchParams] = useState(() => {
if (typeof location === "undefined") {
return serverSearchParams;
}
return new URLSearchParams(location.search);
});
useEffect(() => {
function onPopState() {
setSearchParams(new URLSearchParams(location.search));
}
function onEmitterUpdate(search) {
setSearchParams(search);
}
emitter.on("update", onEmitterUpdate);
window.addEventListener("popstate", onPopState);
return () => {
emitter.off("update", onEmitterUpdate);
window.removeEventListener("popstate", onPopState);
};
}, []);
return searchParams;
}
patchHistory(emitter, adapter);
return {
NuqsAdapter: createAdapterProvider(useNuqsReactRouterBasedAdapter),
useOptimisticSearchParams
};
}
export { createReactRouterBasedAdapter };