import { useSyncExternalStore } from './react-deps.js'; /** * 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 };