1- import { h , createContext , cloneElement , toChildArray } from 'preact' ;
1+ import { h , Fragment , createContext , cloneElement , toChildArray } from 'preact' ;
22import { useContext , useMemo , useReducer , useLayoutEffect , useRef } from 'preact/hooks' ;
33
44/**
@@ -47,10 +47,10 @@ export const exec = (url, route, matches) => {
4747 url = url . split ( '/' ) . filter ( Boolean ) ;
4848 route = ( route || '' ) . split ( '/' ) . filter ( Boolean ) ;
4949 for ( let i = 0 , val , rest ; i < Math . max ( url . length , route . length ) ; i ++ ) {
50- let [ , m , param , flag ] = ( route [ i ] || '' ) . match ( / ^ ( : ? ) ( .* ?) ( [ + * ? ] ? ) $ / ) ;
50+ let [ , m , pathParam , flag ] = ( route [ i ] || '' ) . match ( / ^ ( : ? ) ( .* ?) ( [ + * ? ] ? ) $ / ) ;
5151 val = url [ i ] ;
5252 // segment match:
53- if ( ! m && param == val ) continue ;
53+ if ( ! m && pathParam == val ) continue ;
5454 // /foo/* match
5555 if ( ! m && val && flag == '*' ) {
5656 matches . rest = '/' + url . slice ( i ) . map ( decodeURIComponent ) . join ( '/' ) ;
@@ -63,8 +63,8 @@ export const exec = (url, route, matches) => {
6363 if ( rest ) val = url . slice ( i ) . map ( decodeURIComponent ) . join ( '/' ) ;
6464 // normal/optional field:
6565 else if ( val ) val = decodeURIComponent ( val ) ;
66- matches . params [ param ] = val ;
67- if ( ! ( param in matches ) ) matches [ param ] = val ;
66+ matches . pathParams [ pathParam ] = val ;
67+ if ( ! ( pathParam in matches ) ) matches [ pathParam ] = val ;
6868 if ( rest ) break ;
6969 }
7070 return matches ;
@@ -74,19 +74,22 @@ export function LocationProvider(props) {
7474 const [ url , route ] = useReducer ( UPDATE , location . pathname + location . search ) ;
7575 const wasPush = push === true ;
7676
77+ /** @type {import('./router.d.ts').LocationHook } */
7778 const value = useMemo ( ( ) => {
7879 const u = new URL ( url , location . origin ) ;
7980 const path = u . pathname . replace ( / \/ + $ / g, '' ) || '/' ;
80- // @ts -ignore-next
81+
8182 return {
8283 url,
8384 path,
84- query : Object . fromEntries ( u . searchParams ) ,
85+ pathParams : { } ,
86+ searchParams : Object . fromEntries ( u . searchParams ) ,
8587 route : ( url , replace ) => route ( { url, replace } ) ,
8688 wasPush
8789 } ;
8890 } , [ url ] ) ;
8991
92+
9093 useLayoutEffect ( ( ) => {
9194 addEventListener ( 'click' , route ) ;
9295 addEventListener ( 'popstate' , route ) ;
@@ -97,7 +100,6 @@ export function LocationProvider(props) {
97100 } ;
98101 } , [ ] ) ;
99102
100- // @ts -ignore
101103 return h ( LocationProvider . ctx . Provider , { value } , props . children ) ;
102104}
103105
@@ -106,8 +108,7 @@ const RESOLVED = Promise.resolve();
106108export function Router ( props ) {
107109 const [ c , update ] = useReducer ( c => c + 1 , 0 ) ;
108110
109- const { url, query, wasPush, path } = useLocation ( ) ;
110- const { rest = path , params = { } } = useContext ( RouteContext ) ;
111+ const { url, path, pathParams, searchParams, wasPush } = useLocation ( ) ;
111112
112113 const isLoading = useRef ( false ) ;
113114 const prevRoute = useRef ( path ) ;
@@ -129,7 +130,7 @@ export function Router(props) {
129130
130131 let pathRoute , defaultRoute , matchProps ;
131132 toChildArray ( props . children ) . some ( ( /** @type {VNode<any> } */ vnode ) => {
132- const matches = exec ( rest , vnode . props . path , ( matchProps = { ...vnode . props , path : rest , query , params , rest : '' } ) ) ;
133+ const matches = exec ( path , vnode . props . path , ( matchProps = { ...vnode . props , path, pathParams , searchParams , rest : '' } ) ) ;
133134 if ( matches ) return ( pathRoute = cloneElement ( vnode , matchProps ) ) ;
134135 if ( vnode . props . default ) defaultRoute = cloneElement ( vnode , matchProps ) ;
135136 } ) ;
@@ -140,7 +141,7 @@ export function Router(props) {
140141 prev . current = cur . current ;
141142
142143 // Only mark as an update if the route component changed.
143- const outgoing = prev . current && prev . current . props . children ;
144+ const outgoing = prev . current ;
144145 if ( ! outgoing || ! incoming || incoming . type !== outgoing . type || incoming . props . component !== outgoing . props . component ) {
145146 // This hack prevents Preact from diffing when we swap `cur` to `prev`:
146147 if ( this . __v && this . __v . __k ) this . __v . __k . reverse ( ) ;
@@ -152,7 +153,8 @@ export function Router(props) {
152153 const isHydratingSuspense = cur . current && cur . current . __u & MODE_HYDRATE && cur . current . __u & MODE_SUSPENDED ;
153154 const isHydratingBool = cur . current && cur . current . __h ;
154155 // @ts -ignore
155- cur . current = /** @type {VNode<any> } */ ( h ( RouteContext . Provider , { value : matchProps } , incoming ) ) ;
156+ // TODO: Figure out how to set `.__h` properly so that it's preserved for the next render.
157+ cur . current = h ( Fragment , { } , incoming ) ;
156158 if ( isHydratingSuspense ) {
157159 cur . current . __u |= MODE_HYDRATE ;
158160 cur . current . __u |= MODE_SUSPENDED ;
@@ -254,11 +256,7 @@ Router.Provider = LocationProvider;
254256LocationProvider . ctx = createContext (
255257 /** @type {import('./router.d.ts').LocationHook & { wasPush: boolean } } */ ( { } )
256258) ;
257- const RouteContext = createContext (
258- /** @type {import('./router.d.ts').RouteHook & { rest: string } } */ ( { } )
259- ) ;
260259
261260export const Route = props => h ( props . component , props ) ;
262261
263262export const useLocation = ( ) => useContext ( LocationProvider . ctx ) ;
264- export const useRoute = ( ) => useContext ( RouteContext ) ;
0 commit comments