Skip to content

Commit 294ba97

Browse files
committed
router: proper path params during loading
1 parent 69a9da4 commit 294ba97

File tree

4 files changed

+76
-11
lines changed

4 files changed

+76
-11
lines changed

packages/router/src/ActivePageContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { useContext } from 'react'
22

3+
import { LocationContextType } from './location'
34
import { createNamedContext } from './util'
45

56
export type LoadingState = 'PRE_SHOW' | 'SHOW_LOADING' | 'DONE'
@@ -8,6 +9,7 @@ export type LoadingStateRecord = Record<
89
| {
910
state: LoadingState
1011
page: React.ComponentType<unknown>
12+
location: LocationContextType
1113
}
1214
| undefined
1315
>

packages/router/src/__tests__/router.test.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ beforeEach(() => {
9696
Object.keys(routes).forEach((key) => delete routes[key])
9797
})
9898

99-
describe.only('slow imports', () => {
99+
describe('slow imports', () => {
100100
const HomePagePlaceholder = () => <>HomePagePlaceholder</>
101101
const AboutPagePlaceholder = () => <>AboutPagePlaceholder</>
102102
const ParamPagePlaceholder = () => <>ParamPagePlaceholder</>
@@ -246,7 +246,7 @@ describe.only('slow imports', () => {
246246
})
247247
})
248248

249-
test.only('useLocation', async () => {
249+
test('useLocation', async () => {
250250
act(() => navigate('/location'))
251251
const screen = render(<TestRouter />)
252252
await waitFor(() => screen.getByText('Location Page'))
@@ -271,6 +271,44 @@ describe.only('slow imports', () => {
271271
// ...followed by the actual page
272272
await waitFor(() => screen.getByText('About Page'))
273273
})
274+
275+
test('path params should never be empty', async () => {
276+
const PathParamPage = ({ value }) => {
277+
expect(value).not.toBeFalsy()
278+
return <p>{value}</p>
279+
}
280+
281+
const TestRouter = () => (
282+
<Router pageLoadingDelay={100}>
283+
<Route
284+
path="/about"
285+
page={AboutPage}
286+
name="about"
287+
whileLoadingPage={AboutPagePlaceholder}
288+
/>
289+
<Route
290+
path="/path-param-test/{value}"
291+
page={PathParamPage}
292+
name="params"
293+
whileLoadingPage={ParamPagePlaceholder}
294+
/>
295+
</Router>
296+
)
297+
298+
act(() => navigate('/path-param-test/test_value'))
299+
const screen = render(<TestRouter />)
300+
301+
// First we render the path parameter value "test_value"
302+
await waitFor(() => screen.getByText('test_value'))
303+
304+
act(() => navigate('/about'))
305+
// After navigating we should keep displaying the old path value...
306+
await waitFor(() => screen.getByText('test_value'))
307+
// ...until we switch over to render the about page loading component...
308+
await waitFor(() => screen.getByText('AboutPagePlaceholder'))
309+
// ...followed by the actual page
310+
await waitFor(() => screen.getByText('About Page'))
311+
})
274312
})
275313

276314
describe('inits routes and navigates as expected', () => {

packages/router/src/active-route-loader.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,12 @@ export const ActiveRouteLoader = ({
4242
const announcementRef = useRef<HTMLDivElement>(null)
4343
const waitingFor = useRef<string>('')
4444
const [loadingState, setLoadingState] = useState<LoadingStateRecord>({
45-
[path]: { page: ArlNullPage, state: 'PRE_SHOW' },
45+
[path]: { page: ArlNullPage, state: 'PRE_SHOW', location },
4646
})
4747
const [renderedChildren, setRenderedChildren] = useState<
4848
React.ReactNode | undefined
4949
>(children)
5050
const [renderedPath, setRenderedPath] = useState(path)
51-
const [renderedLocation, setRenderedLocation] = useState({ ...location })
5251
const isMounted = useIsMounted()
5352

5453
const clearLoadingTimeout = () => {
@@ -82,6 +81,7 @@ export const ActiveRouteLoader = ({
8281
[path]: {
8382
page: ArlNullPage,
8483
state: 'PRE_SHOW',
84+
location: { ...location },
8585
},
8686
}))
8787

@@ -96,11 +96,11 @@ export const ActiveRouteLoader = ({
9696
[path]: {
9797
page: whileLoadingPage || ArlWhileLoadingNullPage,
9898
state: 'SHOW_LOADING',
99+
location: { ...location },
99100
},
100101
}))
101102
setRenderedChildren(children)
102103
setRenderedPath(path)
103-
setRenderedLocation({ ...location })
104104
})
105105
}, delay)
106106

@@ -120,9 +120,9 @@ export const ActiveRouteLoader = ({
120120
[path]: {
121121
page: module.default,
122122
state: 'DONE',
123+
location: { ...location },
123124
},
124125
}))
125-
setRenderedLocation({ ...location })
126126
setRenderedChildren(children)
127127
setRenderedPath(path)
128128
setPageName(name)
@@ -133,6 +133,20 @@ export const ActiveRouteLoader = ({
133133
if (spec.name !== waitingFor.current) {
134134
clearLoadingTimeout()
135135
startPageLoadTransition(spec, delay)
136+
} else {
137+
// Handle navigating to the same page again, but with different path params
138+
setLoadingState((loadingState) => {
139+
const page = loadingState[path]?.page || ArlNullPage
140+
141+
return {
142+
...loadingState,
143+
[path]: {
144+
page,
145+
state: 'DONE',
146+
location: { ...location },
147+
},
148+
}
149+
})
136150
}
137151

138152
return () => {
@@ -153,6 +167,7 @@ export const ActiveRouteLoader = ({
153167
[path]: {
154168
state: 'DONE',
155169
page: PageFromLoader,
170+
location: { ...location },
156171
},
157172
}
158173

@@ -170,10 +185,15 @@ export const ActiveRouteLoader = ({
170185
}
171186

172187
return (
173-
<ParamsProvider path={renderedPath} location={renderedLocation}>
188+
<ParamsProvider
189+
path={renderedPath}
190+
location={loadingState[renderedPath]?.location}
191+
>
174192
<ActivePageContextProvider value={{ loadingState }}>
175193
<PageLoadingContextProvider
176-
value={{ loading: loadingState[path]?.state === 'SHOW_LOADING' }}
194+
value={{
195+
loading: loadingState[renderedPath]?.state === 'SHOW_LOADING',
196+
}}
177197
>
178198
{renderedChildren}
179199
{loadingState[path]?.state === 'DONE' && (

packages/router/src/router.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ const InternalRoute: React.VFC<InternalRouteProps> = ({
7373
redirect,
7474
notfound,
7575
}) => {
76-
const location = useLocation()
7776
const routerState = useRouterState()
7877
const activePageContext = useActivePageContext()
7978

@@ -89,6 +88,12 @@ const InternalRoute: React.VFC<InternalRouteProps> = ({
8988
// Check for issues with the path.
9089
validatePath(path)
9190

91+
const location = activePageContext.loadingState[path]?.location
92+
93+
if (!location) {
94+
throw new Error(`No location for route "${name}"`)
95+
}
96+
9297
const { params: pathParams } = matchPath(
9398
path,
9499
location.pathname,
@@ -240,7 +245,7 @@ const LocationAwareRouter: React.FC<RouterProps> = ({
240245
return (
241246
<RouterContextProvider useAuth={useAuth} paramTypes={paramTypes}>
242247
{redirect && <Redirect to={replaceParams(redirect, allParams)} />}
243-
{!redirect && (
248+
{!redirect && page && (
244249
<ActiveRouteLoader
245250
path={path}
246251
spec={normalizePage(page)}
@@ -272,7 +277,7 @@ function analyzeRouterTree(
272277
paramTypes?: Record<string, ParamType>
273278
): {
274279
root: React.ReactElement | undefined
275-
activeRoute: React.ReactElement | undefined
280+
activeRoute: React.ReactElement<InternalRouteProps> | undefined
276281
NotFoundPage: PageType | undefined
277282
} {
278283
let NotFoundPage: PageType | undefined = undefined

0 commit comments

Comments
 (0)