11import { h , createContext , cloneElement } from 'preact' ;
2- import { useContext , useMemo , useReducer , useEffect , useRef } from 'preact/hooks' ;
2+ import { useContext , useMemo , useReducer , useEffect , useLayoutEffect , useRef } from 'preact/hooks' ;
33
44const UPDATE = ( state , url , push ) => {
55 if ( url && url . type === 'click' ) {
@@ -18,11 +18,11 @@ const UPDATE = (state, url, push) => {
1818 return url ;
1919} ;
2020
21- const exec = ( url , route , matches ) => {
21+ export const exec = ( url , route , matches ) => {
2222 url = url . trim ( '/' ) . split ( '/' ) ;
2323 route = ( route || '' ) . trim ( '/' ) . split ( '/' ) ;
2424 for ( let i = 0 , val ; i < Math . max ( url . length , route . length ) ; i ++ ) {
25- let [ , m , param , flag ] = ( route [ i ] || '' ) . match ( / ^ ( \ :? ) ( .* ?) ( [ + * ? ] ? ) $ / ) ;
25+ let [ , m , param , flag ] = ( route [ i ] || '' ) . match ( / ^ ( : ? ) ( .* ?) ( [ + * ? ] ? ) $ / ) ;
2626 val = url [ i ] ;
2727 // segment match:
2828 if ( ! m && param == val ) continue ;
@@ -58,7 +58,7 @@ export function LocationProvider(props) {
5858 removeEventListener ( 'click' , route ) ;
5959 removeEventListener ( 'popstate' , route ) ;
6060 } ;
61- } ) ;
61+ } , [ ] ) ;
6262
6363 // @ts -ignore
6464 return h ( LocationProvider . ctx . Provider , { value } , props . children ) ;
@@ -77,27 +77,39 @@ export function Router(props) {
7777 const prevChildren = useRef ( ) ;
7878 const pending = useRef ( ) ;
7979
80+ let reverse = false ;
8081 if ( url !== cur . current . url ) {
82+ reverse = true ;
8183 pending . current = null ;
8284 prev . current = cur . current ;
8385 prevChildren . current = curChildren . current ;
86+ // old <Committer> uses the pending promise ref to know whether to render
87+ prevChildren . current . props . pending = pending ;
8488 cur . current = loc ;
8589 }
8690
91+ curChildren . current = useMemo ( ( ) => {
92+ let p , d , m ;
93+ [ ] . concat ( props . children || [ ] ) . some ( vnode => {
94+ const matches = exec ( path , vnode . props . path , ( m = { path, query } ) ) ;
95+ if ( matches ) return ( p = cloneElement ( vnode , m ) ) ;
96+ if ( vnode . props . default ) d = cloneElement ( vnode , m ) ;
97+ } ) ;
98+
99+ return h ( Committer , { } , h ( RouteContext . Provider , { value : m } , p || d ) ) ;
100+ } , [ url ] ) ;
101+
87102 this . componentDidCatch = err => {
88- if ( err && err . then ) {
89- // Trigger an update so the rendering login will pickup the pending promise.
90- update ( 1 ) ;
91- pending . current = err ;
92- }
103+ if ( err && err . then ) pending . current = err ;
93104 } ;
94105
95- useEffect ( ( ) => {
106+ useLayoutEffect ( ( ) => {
96107 let p = pending . current ;
108+
97109 const commit = ( ) => {
98110 if ( cur . current . url !== url || pending . current !== p ) return ;
111+ prev . current = prevChildren . current = pending . current = null ;
99112 if ( props . onLoadEnd ) props . onLoadEnd ( url ) ;
100- prev . current = prevChildren . current = null ;
101113 update ( 0 ) ;
102114 } ;
103115
@@ -107,27 +119,20 @@ export function Router(props) {
107119 } else commit ( ) ;
108120 } , [ url ] ) ;
109121
110- let p , d , m ;
111- [ ] . concat ( props . children || [ ] ) . some ( vnode => {
112- const matches = exec ( path , vnode . props . path , ( m = { path, query } ) ) ;
113- if ( matches ) {
114- return ( p = (
115- < RouteContext . Provider value = { { ...matches } } >
116- { cloneElement ( vnode , { ...m , ...matches } ) }
117- </ RouteContext . Provider >
118- ) ) ;
119- }
120- if ( vnode . props . default ) d = cloneElement ( vnode , m ) ;
121- return undefined ;
122- } ) ;
123-
124- return [ ( curChildren . current = p || d ) , prevChildren . current ] ;
122+ // Hi! Wondering what this horrid line is for? That's totally reasonable, it is gross.
123+ // It prevents the old route from being remounted because it got shifted in the children Array.
124+ if ( reverse && this . __v && this . __v . __k ) this . __v . __k . reverse ( ) ;
125+
126+ return [ curChildren . current , prevChildren . current ] ;
127+ }
128+
129+ function Committer ( { pending, children } ) {
130+ return pending && ! pending . current ? null : children ;
125131}
126132
127133Router . Provider = LocationProvider ;
128134
129135LocationProvider . ctx = createContext ( /** @type {{ url: string, path: string, query: object, route } } */ ( { } ) ) ;
130-
131136const RouteContext = createContext ( { } ) ;
132137
133138export const useLocation = ( ) => useContext ( LocationProvider . ctx ) ;
0 commit comments