diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts index c81a2d3707..09eac0f673 100644 --- a/packages/react-router/src/router.ts +++ b/packages/react-router/src/router.ts @@ -1938,21 +1938,12 @@ export class Router< if (match.isFetching === 'beforeLoad') { // If the user doesn't want the route to reload, just // resolve with the existing loader data + // Otherwise, load the route // if (match.fetchCount && match.status === 'success') { // resolve() // } - // Otherwise, load the route - matches[index] = match = updateMatch( - match.id, - (prev) => ({ - ...prev, - isFetching: 'loader', - fetchCount: match.fetchCount + 1, - }), - ) - lazyPromise = route.lazyFn?.().then((lazyRoute) => { Object.assign(route.options, lazyRoute.options) @@ -1978,6 +1969,15 @@ export class Router< // we can use the options await lazyPromise + matches[index] = match = updateMatch( + match.id, + (prev) => ({ + ...prev, + isFetching: 'loader', + fetchCount: match.fetchCount + 1, + }), + ) + checkLatest() // Kick off the loader! diff --git a/packages/react-router/tests/components/mockHeavyDependenciesRoute.tsx b/packages/react-router/tests/components/mockHeavyDependenciesRoute.tsx new file mode 100644 index 0000000000..28e30f9b26 --- /dev/null +++ b/packages/react-router/tests/components/mockHeavyDependenciesRoute.tsx @@ -0,0 +1,6 @@ +// This mimicks the waiting of heavy dependencies, which need to be streamed in before the component is available. +await new Promise((resolve) => setTimeout(resolve, 5000)) + +export default function HeavyComponent() { + return

I am sooo heavy

+} diff --git a/packages/react-router/tests/createLazyRoute.test.tsx b/packages/react-router/tests/createLazyRoute.test.tsx new file mode 100644 index 0000000000..da5bedf116 --- /dev/null +++ b/packages/react-router/tests/createLazyRoute.test.tsx @@ -0,0 +1,59 @@ +import { afterEach, describe, expect, it, vi } from 'vitest' +import { + RouterHistory, + createMemoryHistory, + createRootRoute, + createRoute, + createRouter, +} from '../src' +import { cleanup } from '@testing-library/react' +import { act } from 'react' + +afterEach(() => { + vi.resetAllMocks() + cleanup() +}) + +function createTestRouter(initialHistory?: RouterHistory) { + const history = + initialHistory ?? createMemoryHistory({ initialEntries: ['/'] }) + + const rootRoute = createRootRoute({}) + const indexRoute = createRoute({ getParentRoute: () => rootRoute, path: '/' }) + + const heavyRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/heavy', + }).lazy(() => import('./lazy-routes/heavy').then((d) => d.default('/heavy'))) + + const routeTree = rootRoute.addChildren([indexRoute, heavyRoute]) + + const router = createRouter({ routeTree, history }) + + return { + router, + routes: { indexRoute, heavyRoute }, + } +} + +describe('preload: matched routes', { timeout: 20000 }, () => { + it('should wait for lazy options to be streamed in before ', async () => { + const { router } = createTestRouter( + createMemoryHistory({ initialEntries: ['/'] }), + ) + + await router.load() + + // Preload the route and navigate to it + router.preloadRoute({ to: '/heavy' }) + await router.navigate({ to: '/heavy' }) + + await router.invalidate() + + expect(router.state.location.pathname).toBe('/heavy') + + const lazyRoute = router.routesByPath['/heavy'] + + expect(lazyRoute.options.component).toBeDefined() + }) +}) diff --git a/packages/react-router/tests/lazy-routes/heavy.tsx b/packages/react-router/tests/lazy-routes/heavy.tsx new file mode 100644 index 0000000000..4fbb170742 --- /dev/null +++ b/packages/react-router/tests/lazy-routes/heavy.tsx @@ -0,0 +1,8 @@ +import { createLazyRoute } from '../../src' +import HeavyComponent from '../components/mockHeavyDependenciesRoute' + +export default function Route(id: string) { + return createLazyRoute(id)({ + component: HeavyComponent, + }) +}