diff --git a/packages/react-router/tests/loaders.test.tsx b/packages/react-router/tests/loaders.test.tsx index 7eccb96a13..8f6c63b89b 100644 --- a/packages/react-router/tests/loaders.test.tsx +++ b/packages/react-router/tests/loaders.test.tsx @@ -729,3 +729,76 @@ test('clears pendingTimeout when match resolves', async () => { expect(nestedPendingComponentOnMountMock).not.toHaveBeenCalled() expect(fooPendingComponentOnMountMock).not.toHaveBeenCalled() }) + + +test('reproducer #4998 - beforeLoad is awaited before rendering', async () => { + const beforeLoad = vi.fn() + const select = vi.fn() + let resolved = 0 + const rootRoute = createRootRoute() + const indexRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/', + component: () => To foo, + }) + const fooRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/foo', + beforeLoad: async (...args) => { + beforeLoad(...args) + await new Promise((resolve) => setTimeout(resolve, 20)) + resolved += 1 + return { foo: 'bar' } + }, + component: () => { + fooRoute.useRouteContext({ select }) + return

Foo index page

+ }, + pendingComponent: () => 'loading', + }) + const routeTree = rootRoute.addChildren([ + indexRoute, + fooRoute + ]) + const router = createRouter({ + routeTree, + history, + defaultPreload: 'intent', + defaultPendingMs: 0, + }) + + render() + const linkToFoo = await screen.findByText('To foo') + + fireEvent.focus(linkToFoo) + await sleep(100) + + expect(resolved).toBe(1) + + expect(beforeLoad).toHaveBeenCalledTimes(1) + expect(beforeLoad).toHaveBeenNthCalledWith(1, expect.objectContaining({ + cause: 'preload', + preload: true, + })) + + expect(select).not.toHaveBeenCalled() + + fireEvent.click(linkToFoo) + await screen.findByText('Foo index page') + + expect(beforeLoad).toHaveBeenCalledTimes(2) + expect(beforeLoad).toHaveBeenNthCalledWith(2, expect.objectContaining({ + cause: 'enter', + preload: false, + })) + + expect(select).toHaveBeenNthCalledWith(1, expect.objectContaining({ + foo: 'bar', + })) + + // I'm not 100% sure this should be 2 here, + // maybe we can re-use the cached beforeLoad from preload in some cases + // but since we assert a 2nd beforeLoad call above, + // then we should have awaited its resolution too + expect(resolved).toBe(2) +}) \ No newline at end of file