wouter-preact
Version:
Minimalist-friendly ~1.5KB router for Preact
86 lines (70 loc) • 2.74 kB
JavaScript
import { a as useSyncExternalStore } from './preact-deps-dec5c677.js';
import 'preact/hooks';
import 'preact';
/**
* History API docs @see https://developer.mozilla.org/en-US/docs/Web/API/History
*/
const eventPopstate = "popstate";
const eventPushState = "pushState";
const eventReplaceState = "replaceState";
const eventHashchange = "hashchange";
const events = [
eventPopstate,
eventPushState,
eventReplaceState,
eventHashchange,
];
const subscribeToLocationUpdates = (callback) => {
for (const event of events) {
addEventListener(event, callback);
}
return () => {
for (const event of events) {
removeEventListener(event, callback);
}
};
};
const useLocationProperty = (fn, ssrFn) =>
useSyncExternalStore(subscribeToLocationUpdates, fn, ssrFn);
const currentSearch = () => location.search;
const useSearch = ({ ssrSearch = "" } = {}) =>
useLocationProperty(currentSearch, () => ssrSearch);
const currentPathname = () => location.pathname;
const usePathname = ({ ssrPath } = {}) =>
useLocationProperty(
currentPathname,
ssrPath ? () => ssrPath : currentPathname
);
const currentHistoryState = () => history.state;
const useHistoryState = () =>
useLocationProperty(currentHistoryState, () => null);
const navigate = (to, { replace = false, state = null } = {}) =>
history[replace ? eventReplaceState : eventPushState](state, "", to);
// the 2nd argument of the `useBrowserLocation` return value is a function
// that allows to perform a navigation.
const useBrowserLocation = (opts = {}) => [usePathname(opts), navigate];
const patchKey = Symbol.for("wouter_v3");
// While History API does have `popstate` event, the only
// proper way to listen to changes via `push/replaceState`
// is to monkey-patch these methods.
//
// See https://stackoverflow.com/a/4585031
if (typeof history !== "undefined" && typeof window[patchKey] === "undefined") {
for (const type of [eventPushState, eventReplaceState]) {
const original = history[type];
// TODO: we should be using unstable_batchedUpdates to avoid multiple re-renders,
// however that will require an additional peer dependency on react-dom.
// See: https://github.com/reactwg/react-18/discussions/86#discussioncomment-1567149
history[type] = function () {
const result = original.apply(this, arguments);
const event = new Event(type);
event.arguments = arguments;
dispatchEvent(event);
return result;
};
}
// patch history object only once
// See: https://github.com/molefrog/wouter/issues/167
Object.defineProperty(window, patchKey, { value: true });
}
export { navigate, useBrowserLocation, useHistoryState, useLocationProperty, usePathname, useSearch };