diff --git a/.changeset/fix-beforeload-null-context.md b/.changeset/fix-beforeload-null-context.md new file mode 100644 index 00000000000..5399fc50176 --- /dev/null +++ b/.changeset/fix-beforeload-null-context.md @@ -0,0 +1,9 @@ +--- +'@tanstack/router-core': patch +--- + +fix(router-core): treat null return from beforeLoad as no-op for route context + +When `beforeLoad` returns `null`, it is now treated the same as `undefined` (no-op) instead of being stored as `__beforeLoadContext: null`. This is consistent with how the `context` route option already handles null returns via `?? undefined`, and prevents null from silently interfering with context accumulation. + +Fixes #7110 diff --git a/packages/react-router/tests/routeContext.test.tsx b/packages/react-router/tests/routeContext.test.tsx index d11f86420e6..daaa0656494 100644 --- a/packages/react-router/tests/routeContext.test.tsx +++ b/packages/react-router/tests/routeContext.test.tsx @@ -2630,6 +2630,42 @@ describe('useRouteContext in the component', () => { expect(content).toBeInTheDocument() }) + test('route context preserved when beforeLoad returns null', async () => { + const rootRoute = createRootRoute({ + beforeLoad: () => null, + component: () => { + const context = rootRoute.useRouteContext() + return
{JSON.stringify(context)}
+ }, + }) + const routeTree = rootRoute.addChildren([]) + const router = createRouter({ routeTree, history, context: { foo: 'bar' } }) + + render() + + const content = await screen.findByText(JSON.stringify({ foo: 'bar' })) + + expect(content).toBeInTheDocument() + }) + + test('route context preserved when async beforeLoad returns null', async () => { + const rootRoute = createRootRoute({ + beforeLoad: async () => null, + component: () => { + const context = rootRoute.useRouteContext() + return
{JSON.stringify(context)}
+ }, + }) + const routeTree = rootRoute.addChildren([]) + const router = createRouter({ routeTree, history, context: { foo: 'bar' } }) + + render() + + const content = await screen.findByText(JSON.stringify({ foo: 'bar' })) + + expect(content).toBeInTheDocument() + }) + test('route context (sleep in beforeLoad), present in the root route', async () => { const rootRoute = createRootRoute({ beforeLoad: async () => { diff --git a/packages/router-core/src/load-matches.ts b/packages/router-core/src/load-matches.ts index 3e99c828936..a2b6d798cf6 100644 --- a/packages/router-core/src/load-matches.ts +++ b/packages/router-core/src/load-matches.ts @@ -490,7 +490,7 @@ const executeBeforeLoad = ( } const updateContext = (beforeLoadContext: any) => { - if (beforeLoadContext === undefined) { + if (beforeLoadContext === undefined || beforeLoadContext === null) { inner.router.batch(() => { pending() resolve()