diff --git a/.changeset/fast-suns-flow.md b/.changeset/fast-suns-flow.md new file mode 100644 index 000000000..e2dc49064 --- /dev/null +++ b/.changeset/fast-suns-flow.md @@ -0,0 +1,5 @@ +--- +'preact-iso': patch +--- + +Use pushState/ replaceState for useLocation().route method diff --git a/packages/preact-iso/router.d.ts b/packages/preact-iso/router.d.ts index d6e227f3a..3bec3a12b 100644 --- a/packages/preact-iso/router.d.ts +++ b/packages/preact-iso/router.d.ts @@ -8,7 +8,7 @@ interface LocationHook { url: string; path: string; query: Record; - route: (url: string) => void; + route: (url: string | { url: string, replace?: boolean }) => void; } export const useLocation: () => LocationHook; diff --git a/packages/preact-iso/router.js b/packages/preact-iso/router.js index 7106f4d6c..7f9f860df 100644 --- a/packages/preact-iso/router.js +++ b/packages/preact-iso/router.js @@ -1,17 +1,34 @@ import { h, createContext, cloneElement } from 'preact'; -import { useContext, useMemo, useReducer, useEffect, useLayoutEffect, useRef } from 'preact/hooks'; - -const UPDATE = (state, url) => { - let push = true; - if (url && url.type === 'click') { - const link = url.target.closest('a[href]'); +import { useContext, useMemo, useCallback, useReducer, useEffect, useLayoutEffect, useRef } from 'preact/hooks'; + +/** + * @param {string} state + * @param {MouseEvent|PopStateEvent|Object|string} update + * @return {string|undefined} + */ +const UPDATE = (state, update) => { + /** @type {boolean|undefined} - History state update strategy */ + let push, url; + + if (!update || typeof update === 'string') { + // manual invocation: route(url) + url = update; + push = true; + } else if (update.type === 'click') { + // user click + const link = update.target.closest('a[href]'); if (!link || link.origin != location.origin) return state; - url.preventDefault(); - url = link.href.replace(location.origin, ''); - } else if (typeof url !== 'string') { - url = location.pathname + location.search; - push = undefined; + update.preventDefault(); + url = link.pathname + link.search + link.hash; + push = true; + } else if (update.type === 'popstate') { + // navigation + url = location.pathname + location.search + location.hash; + } else { + // manual invocation: route({ url, replace }) + url = update.url || update; + push = !url.replace; } if (push === true) history.pushState(null, '', url); @@ -42,7 +59,8 @@ export const exec = (url, route, matches) => { }; export function LocationProvider(props) { - const [url, route] = useReducer(UPDATE, location.pathname + location.search); + const [url, doRoute] = useReducer(UPDATE, location.pathname + location.search); + const route = useCallback((url, replace) => doRoute({ url, replace }), []); const value = useMemo(() => { const u = new URL(url, location.origin);