From bdbf5e857bb75992e536f24b9c9d4f281a9440ee Mon Sep 17 00:00:00 2001 From: "Gianmarco Rengucci (freshgiammi)" Date: Tue, 2 Jul 2024 12:47:07 +0200 Subject: [PATCH 1/2] fix: wait for lazy route to resolve before firing loader --- packages/react-router/src/router.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts index c81a2d37076..09eac0f6731 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! From bb19c1b0203211daddc689d7e56d3e625ee8b3f9 Mon Sep 17 00:00:00 2001 From: "Gianmarco Rengucci (freshgiammi)" Date: Wed, 3 Jul 2024 12:59:30 +0200 Subject: [PATCH 2/2] test: detect lazy routes loaded too early --- .../components/mockHeavyDependenciesRoute.tsx | 6 ++ .../tests/createLazyRoute.test.tsx | 59 +++++++++++++++++++ .../react-router/tests/lazy-routes/heavy.tsx | 8 +++ 3 files changed, 73 insertions(+) create mode 100644 packages/react-router/tests/components/mockHeavyDependenciesRoute.tsx create mode 100644 packages/react-router/tests/createLazyRoute.test.tsx create mode 100644 packages/react-router/tests/lazy-routes/heavy.tsx diff --git a/packages/react-router/tests/components/mockHeavyDependenciesRoute.tsx b/packages/react-router/tests/components/mockHeavyDependenciesRoute.tsx new file mode 100644 index 00000000000..28e30f9b262 --- /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 00000000000..da5bedf116b --- /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 00000000000..4fbb170742a --- /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, + }) +}