Skip to content

Commit

Permalink
feat(react-router): allow to preload route chunk (#3016)
Browse files Browse the repository at this point in the history
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Manuel Schiller <manuel.schiller@caligano.de>
  • Loading branch information
3 people authored Dec 16, 2024
1 parent 3f1c9ac commit 02a4957
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 31 deletions.
6 changes: 6 additions & 0 deletions docs/framework/react/api/router/RouterType.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ Preloads all of the matches that match the provided `NavigateOptions`.
- Returns
- A promise that resolves with an array of all of the route matches that were preloaded.
### `.loadRouteChunk` method
Loads the JS chunk of the route.
- Type: `(route: AnyRoute) => Promise<void>`
### `.matchRoute` method
Matches a pathname and search params against the router's route tree and returns a route match's params or false if no match was found.
Expand Down
23 changes: 23 additions & 0 deletions docs/framework/react/guide/preloading.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,26 @@ function Component() {
return <div />
}
```

If you need to preload only the JS chunk of a route, you can use the router's `loadRouteChunk` method. It accepts a route object and returns a promise that resolves when the route chunk is loaded.

```tsx
function Component() {
const router = useRouter()

useEffect(() => {
try {
const postsRoute = router.routesByPath['/posts']
await Promise.all([
router.loadRouteChunk(router.routesByPath['/']),
router.loadRouteChunk(postsRoute),
router.loadRouteChunk(postsRoute.parentRoute),
])
} catch (err) {
// Failed to preload route chunk
}
}, [])

return <div />
}
```
63 changes: 32 additions & 31 deletions packages/react-router/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2553,37 +2553,7 @@ export class Router<

// Actually run the loader and handle the result
try {
if (route._lazyPromise === undefined) {
if (route.lazyFn) {
route._lazyPromise = route
.lazyFn()
.then((lazyRoute) => {
// explicitly don't copy over the lazy route's id
const { id: _id, ...options } =
lazyRoute.options
Object.assign(route.options, options)
})
} else {
route._lazyPromise = Promise.resolve()
}
}

// If for some reason lazy resolves more lazy components...
// We'll wait for that before pre attempt to preload any
// components themselves.
if (route._componentsPromise === undefined) {
route._componentsPromise = route._lazyPromise.then(
() =>
Promise.all(
componentTypes.map(async (type) => {
const component = route.options[type]
if ((component as any)?.preload) {
await (component as any).preload()
}
}),
),
)
}
this.loadRouteChunk(route)

updateMatch(matchId, (prev) => ({
...prev,
Expand Down Expand Up @@ -2821,6 +2791,37 @@ export class Router<
this.clearCache({ filter })
}

loadRouteChunk = (route: AnyRoute) => {
if (route._lazyPromise === undefined) {
if (route.lazyFn) {
route._lazyPromise = route.lazyFn().then((lazyRoute) => {
// explicitly don't copy over the lazy route's id
const { id: _id, ...options } = lazyRoute.options
Object.assign(route.options, options)
})
} else {
route._lazyPromise = Promise.resolve()
}
}

// If for some reason lazy resolves more lazy components...
// We'll wait for that before pre attempt to preload any
// components themselves.
if (route._componentsPromise === undefined) {
route._componentsPromise = route._lazyPromise.then(() =>
Promise.all(
componentTypes.map(async (type) => {
const component = route.options[type]
if ((component as any)?.preload) {
await (component as any).preload()
}
}),
),
)
}
return route._componentsPromise
}

preloadRoute = async <
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string | undefined = undefined,
Expand Down

0 comments on commit 02a4957

Please sign in to comment.