Skip to content

Commit 895b644

Browse files
committed
refactor: Switch to Navigation API
1 parent 78fd922 commit 895b644

File tree

2 files changed

+17
-47
lines changed

2 files changed

+17
-47
lines changed

src/router.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ interface LocationHook {
4343
path: string;
4444
pathParams: Record<string, string>;
4545
searchParams: Record<string, string>;
46-
route: (url: string, replace?: boolean) => void;
4746
}
4847
export const useLocation: () => LocationHook;
4948

src/router.js

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

10-
/** @type {boolean} */
11-
let push;
1210
/** @type {string | RegExp | undefined} */
1311
let scope;
1412

@@ -23,45 +21,23 @@ function isInScope(href) {
2321
);
2422
}
2523

24+
2625
/**
2726
* @param {string} state
28-
* @param {MouseEvent | PopStateEvent | { url: string, replace?: boolean }} action
27+
* @param {NavigateEvent} e
2928
*/
30-
function handleNav(state, action) {
31-
let url = '';
32-
push = undefined;
33-
if (action && action.type === 'click') {
34-
// ignore events the browser takes care of already:
35-
if (action.ctrlKey || action.metaKey || action.altKey || action.shiftKey || action.button !== 0) {
36-
return state;
37-
}
38-
39-
const link = action.composedPath().find(el => el.nodeName == 'A' && el.href),
40-
href = link && link.getAttribute('href');
41-
if (
42-
!link ||
43-
link.origin != location.origin ||
44-
/^#/.test(href) ||
45-
!/^(_?self)?$/i.test(link.target) ||
46-
!isInScope(href)
47-
) {
48-
return state;
49-
}
29+
function handleNav(state, e) {
30+
if (!e.canIntercept) return state;
31+
if (e.hashChange || e.downloadRequest !== null) return state;
5032

51-
push = true;
52-
action.preventDefault();
53-
url = link.href.replace(location.origin, '');
54-
} else if (action && action.url) {
55-
push = !action.replace;
56-
url = action.url;
57-
} else {
58-
url = location.pathname + location.search;
33+
const url = new URL(e.destination.url);
34+
if (!isInScope(url.href)) {
35+
return state;
5936
}
6037

61-
if (push === true) history.pushState(null, '', url);
62-
else if (push === false) history.replaceState(null, '', url);
63-
return url;
64-
};
38+
e.intercept();
39+
return url.href.replace(url.origin, '');
40+
}
6541

6642
export const exec = (url, route, matches = {}) => {
6743
url = url.split('/').filter(Boolean);
@@ -99,7 +75,6 @@ export const exec = (url, route, matches = {}) => {
9975
export function LocationProvider(props) {
10076
const [url, route] = useReducer(handleNav, location.pathname + location.search);
10177
if (props.scope) scope = props.scope;
102-
const wasPush = push === true;
10378

10479
const value = useMemo(() => {
10580
const u = new URL(url, location.origin);
@@ -110,18 +85,14 @@ export function LocationProvider(props) {
11085
path,
11186
pathParams: {},
11287
searchParams: Object.fromEntries(u.searchParams),
113-
route: (url, replace) => route({ url, replace }),
114-
wasPush
11588
};
11689
}, [url]);
11790

11891
useLayoutEffect(() => {
119-
addEventListener('click', route);
120-
addEventListener('popstate', route);
92+
navigation.addEventListener('navigate', route);
12193

12294
return () => {
123-
removeEventListener('click', route);
124-
removeEventListener('popstate', route);
95+
navigation.removeEventListener('navigate', route);
12596
};
12697
}, []);
12798

@@ -133,7 +104,7 @@ const RESOLVED = Promise.resolve();
133104
export function Router(props) {
134105
const [c, update] = useReducer(c => c + 1, 0);
135106

136-
const { url, path, pathParams, searchParams, wasPush } = useLocation();
107+
const { url, path, pathParams, searchParams } = useLocation();
137108
if (!url) {
138109
throw new Error(`preact-iso's <Router> must be used within a <LocationProvider>, see: https://github.com/preactjs/preact-iso#locationprovider`);
139110
}
@@ -257,15 +228,15 @@ export function Router(props) {
257228

258229
// The route is loaded and rendered.
259230
if (prevRoute.current !== path) {
260-
if (wasPush) scrollTo(0, 0);
231+
scrollTo(0, 0);
261232
if (props.onRouteChange) props.onRouteChange(url);
262233

263234
prevRoute.current = path;
264235
}
265236

266237
if (props.onLoadEnd && isLoading.current) props.onLoadEnd(url);
267238
isLoading.current = false;
268-
}, [path, wasPush, c]);
239+
}, [path, c]);
269240

270241
// Note: cur MUST render first in order to set didSuspend & prev.
271242
return routeChanged
@@ -282,7 +253,7 @@ const RenderRef = ({ r }) => r.current;
282253
Router.Provider = LocationProvider;
283254

284255
LocationProvider.ctx = createContext(
285-
/** @type {import('./router.d.ts').LocationHook & { wasPush: boolean }} */ ({})
256+
/** @type {import('./router.d.ts').LocationHook}} */ ({})
286257
);
287258
const RouterContext = createContext(
288259
/** @type {{ rest: string }} */ ({})

0 commit comments

Comments
 (0)