Skip to content

Commit 671445a

Browse files
committed
refactor: Switch to Navigation API
1 parent 3efae13 commit 671445a

File tree

2 files changed

+28
-50
lines changed

2 files changed

+28
-50
lines changed

src/router.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ interface LocationHook {
4040
path: string;
4141
pathParams: Record<string, string>;
4242
searchParams: Record<string, string>;
43-
route: (url: string, replace?: boolean) => void;
4443
}
4544
export const useLocation: () => LocationHook;
4645

src/router.js

Lines changed: 28 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -7,46 +7,30 @@ import { useContext, useMemo, useReducer, useLayoutEffect, useRef } from 'preact
77
* @typedef {import('./internal.d.ts').VNode} VNode
88
*/
99

10-
let push, scope;
11-
const UPDATE = (state, url) => {
12-
push = undefined;
13-
if (url && url.type === 'click') {
14-
// ignore events the browser takes care of already:
15-
if (url.ctrlKey || url.metaKey || url.altKey || url.shiftKey || url.button !== 0) {
16-
return state;
17-
}
18-
19-
const link = url.target.closest('a[href]'),
20-
href = link && link.getAttribute('href');
21-
if (
22-
!link ||
23-
link.origin != location.origin ||
24-
/^#/.test(href) ||
25-
!/^(_?self)?$/i.test(link.target) ||
26-
scope && (typeof scope == 'string'
27-
? !href.startsWith(scope)
28-
: !scope.test(href)
29-
)
30-
) {
31-
return state;
32-
}
10+
/** @type {string | RegExp | undefined} */
11+
let scope;
3312

34-
push = true;
35-
url.preventDefault();
36-
url = link.href.replace(location.origin, '');
37-
} else if (typeof url === 'string') {
38-
push = true;
39-
} else if (url && url.url) {
40-
push = !url.replace;
41-
url = url.url;
42-
} else {
43-
url = location.pathname + location.search;
13+
/**
14+
* @param {string} state
15+
* @param {NavigateEvent} e
16+
*/
17+
function handleNav(state, e) {
18+
if (!e.canIntercept) return state;
19+
if (e.hashChange || e.downloadRequest !== null) return state;
20+
21+
const url = new URL(e.destination.url);
22+
if (
23+
scope && (typeof scope == 'string'
24+
? !url.pathname.startsWith(scope)
25+
: !scope.test(url.pathname)
26+
)
27+
) {
28+
return state;
4429
}
4530

46-
if (push === true) history.pushState(null, '', url);
47-
else if (push === false) history.replaceState(null, '', url);
48-
return url;
49-
};
31+
e.intercept();
32+
return url.href.replace(url.origin, '');
33+
}
5034

5135
export const exec = (url, route, matches = {}) => {
5236
url = url.split('/').filter(Boolean);
@@ -80,9 +64,8 @@ export const exec = (url, route, matches = {}) => {
8064
* @type {import('./router.d.ts').LocationProvider}
8165
*/
8266
export function LocationProvider(props) {
83-
const [url, route] = useReducer(UPDATE, location.pathname + location.search);
67+
const [url, route] = useReducer(handleNav, location.pathname + location.search);
8468
if (props.scope) scope = props.scope;
85-
const wasPush = push === true;
8669

8770
const value = useMemo(() => {
8871
const u = new URL(url, location.origin);
@@ -93,18 +76,14 @@ export function LocationProvider(props) {
9376
path,
9477
pathParams: {},
9578
searchParams: Object.fromEntries(u.searchParams),
96-
route: (url, replace) => route({ url, replace }),
97-
wasPush
9879
};
9980
}, [url]);
10081

10182
useLayoutEffect(() => {
102-
addEventListener('click', route);
103-
addEventListener('popstate', route);
83+
navigation.addEventListener('navigate', route);
10484

10585
return () => {
106-
removeEventListener('click', route);
107-
removeEventListener('popstate', route);
86+
navigation.removeEventListener('navigate', route);
10887
};
10988
}, []);
11089

@@ -116,7 +95,7 @@ const RESOLVED = Promise.resolve();
11695
export function Router(props) {
11796
const [c, update] = useReducer(c => c + 1, 0);
11897

119-
const { url, path, pathParams, searchParams, wasPush } = useLocation();
98+
const { url, path, pathParams, searchParams } = useLocation();
12099

121100
const isLoading = useRef(false);
122101
const prevRoute = useRef(path);
@@ -236,15 +215,15 @@ export function Router(props) {
236215

237216
// The route is loaded and rendered.
238217
if (prevRoute.current !== path) {
239-
if (wasPush) scrollTo(0, 0);
218+
scrollTo(0, 0);
240219
if (props.onRouteChange) props.onRouteChange(url);
241220

242221
prevRoute.current = path;
243222
}
244223

245224
if (props.onLoadEnd && isLoading.current) props.onLoadEnd(url);
246225
isLoading.current = false;
247-
}, [path, wasPush, c]);
226+
}, [path, c]);
248227

249228
// Note: cur MUST render first in order to set didSuspend & prev.
250229
return routeChanged
@@ -261,7 +240,7 @@ const RenderRef = ({ r }) => r.current;
261240
Router.Provider = LocationProvider;
262241

263242
LocationProvider.ctx = createContext(
264-
/** @type {import('./router.d.ts').LocationHook & { wasPush: boolean }} */ ({})
243+
/** @type {import('./router.d.ts').LocationHook}} */ ({})
265244
);
266245

267246
export const Route = props => h(props.component, props);

0 commit comments

Comments
 (0)