Skip to content

Instantly share code, notes, and snippets.

@kamikat
Last active August 4, 2023 02:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kamikat/5a81c7c084ea511ea9de742929f99ed1 to your computer and use it in GitHub Desktop.
Save kamikat/5a81c7c084ea511ea9de742929f99ed1 to your computer and use it in GitHub Desktop.
Drop-in replacement of `useLocation()`/`useNavigate()` hooks in browser.
import { useCallback, useLayoutEffect, useMemo, useReducer } from "react";
function createHooks() {
const listeners: (() => void)[] = [];
window.addEventListener("popstate", () => {
for (const listener of listeners) {
listener();
}
});
function useLocation(): Location {
const [_updateId, notifyUpdate] = useReducer((a) => a + 1, 0);
useLayoutEffect(() => {
const listener = () => notifyUpdate();
listeners.push(listener);
return () => {
const index = listeners.indexOf(listener);
if (index > -1) {
listeners.splice(index, 1);
}
};
}, [notifyUpdate]);
const location: Location = useMemo(() => {
return { _updateId, ...window.location };
}, [_updateId]);
return location;
}
function useNavigate(options?: { replace?: boolean }) {
const isReplace = options?.replace;
return useCallback(
(path: string) => {
if (isReplace) {
window.history.replaceState(null, "", path);
} else {
window.history.pushState(null, "", path);
}
window.dispatchEvent(new Event("popstate"));
},
[isReplace]
);
}
return { useLocation, useNavigate };
}
const { useLocation, useNavigate } = createHooks();
export { useLocation, useNavigate };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment