From dc61450da83464e9131b5c6d861d736c474d834d Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Mon, 28 Oct 2024 22:05:25 +0100 Subject: [PATCH] fix(react-router): `parentMatchPromise`resolves to parent match --- docs/framework/react/guide/data-loading.md | 2 +- packages/react-router/src/Matches.tsx | 10 +++++++ packages/react-router/src/route.ts | 9 +++++-- packages/react-router/src/router.ts | 3 ++- packages/react-router/tests/loaders.test.tsx | 5 +++- packages/react-router/tests/route.test-d.tsx | 28 ++++++++++++-------- 6 files changed, 41 insertions(+), 16 deletions(-) diff --git a/docs/framework/react/guide/data-loading.md b/docs/framework/react/guide/data-loading.md index 24748a7e97..b966418a27 100644 --- a/docs/framework/react/guide/data-loading.md +++ b/docs/framework/react/guide/data-loading.md @@ -78,7 +78,7 @@ The `loader` function receives a single object with the following properties: - `deps` - The object value returned from the `Route.loaderDeps` function. If `Route.loaderDeps` is not defined, an empty object will be provided instead. - `location` - The current location - `params` - The route's path params -- `parentMatchPromise` - `Promise` or `undefined` +- `parentMatchPromise` - `Promise` or `undefined` - `preload` - Boolean which is `true` when the route is being preloaded instead of loaded - `route` - The route itself diff --git a/packages/react-router/src/Matches.tsx b/packages/react-router/src/Matches.tsx index 36dc9ae25c..a99403938d 100644 --- a/packages/react-router/src/Matches.tsx +++ b/packages/react-router/src/Matches.tsx @@ -121,6 +121,16 @@ export const isMatch = ( return value != null } +export type MakeRouteMatchFromRoute = RouteMatch< + TRoute['types']['id'], + TRoute['types']['fullPath'], + TRoute['types']['allParams'], + TRoute['types']['fullSearchSchema'], + TRoute['types']['loaderData'], + TRoute['types']['allContext'], + TRoute['types']['loaderDeps'] +> + export interface RouteMatch< TRouteId, TFullPath, diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts index 1bc5c88908..3a6aef13ca 100644 --- a/packages/react-router/src/route.ts +++ b/packages/react-router/src/route.ts @@ -11,7 +11,12 @@ import { rootRouteId } from './root' import type * as React from 'react' import type { RootRouteId } from './root' import type { UseNavigateResult } from './useNavigate' -import type { MakeRouteMatch, MakeRouteMatchUnion, RouteMatch } from './Matches' +import type { + MakeRouteMatch, + MakeRouteMatchFromRoute, + MakeRouteMatchUnion, + RouteMatch, +} from './Matches' import type { NavigateOptions, ParsePathParams, ToMaskOptions } from './link' import type { ParsedLocation } from './location' import type { RouteById, RouteIds, RoutePaths } from './routeInfo' @@ -591,7 +596,7 @@ export interface LoaderFnContext< * @deprecated Use `throw redirect({ to: '/somewhere' })` instead **/ navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise> cause: 'preload' | 'enter' | 'stay' route: Route } diff --git a/packages/react-router/src/router.ts b/packages/react-router/src/router.ts index a1030c8d55..da1bf6890b 100644 --- a/packages/react-router/src/router.ts +++ b/packages/react-router/src/router.ts @@ -2299,7 +2299,7 @@ export class Router< } const validResolvedMatches = matches.slice(0, firstBadMatchIndex) - const matchPromises: Array> = [] + const matchPromises: Array> = [] validResolvedMatches.forEach(({ id: matchId, routeId }, index) => { matchPromises.push( @@ -2535,6 +2535,7 @@ export class Router< loaderPromise: undefined, invalid: false, })) + return this.getMatch(matchId)! })(), ) }) diff --git a/packages/react-router/tests/loaders.test.tsx b/packages/react-router/tests/loaders.test.tsx index 5f25c8a893..896ffa1064 100644 --- a/packages/react-router/tests/loaders.test.tsx +++ b/packages/react-router/tests/loaders.test.tsx @@ -134,14 +134,17 @@ describe('loaders parentMatchPromise', () => { path: '/nested', loader: async () => { await sleep(WAIT_TIME) + return 'nested' }, component: () => , }) const fooRoute = createRoute({ getParentRoute: () => nestedRoute, path: '/foo', - loader: ({ parentMatchPromise }) => { + loader: async ({ parentMatchPromise }) => { nestedLoaderMock(parentMatchPromise) + const parentMatch = await parentMatchPromise + expect(parentMatch?.loaderData).toBe('nested') }, component: () =>
Nested Foo page
, }) diff --git a/packages/react-router/tests/route.test-d.tsx b/packages/react-router/tests/route.test-d.tsx index 69286c8c0b..65bdb69b0a 100644 --- a/packages/react-router/tests/route.test-d.tsx +++ b/packages/react-router/tests/route.test-d.tsx @@ -16,7 +16,11 @@ import type { Route, SearchSchemaInput, } from '../src' -import type { MakeRouteMatchUnion } from '../src/Matches' +import type { + MakeRouteMatch, + MakeRouteMatchFromRoute, + MakeRouteMatchUnion, +} from '../src/Matches' test('when creating the root', () => { const rootRoute = createRootRoute() @@ -83,7 +87,7 @@ test('when creating the root with a loader', () => { context: {} location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise cause: 'preload' | 'enter' | 'stay' route: Route }>() @@ -178,7 +182,7 @@ test('when creating the root route with context and a loader', () => { context: { userId: string } location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise cause: 'preload' | 'enter' | 'stay' route: Route }>() @@ -246,7 +250,7 @@ test('when creating the root route with context, routeContext, beforeLoad and a context: { userId: string; permission: 'view'; env: 'env1' } location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise cause: 'preload' | 'enter' | 'stay' route: Route }>() @@ -374,7 +378,7 @@ test('when creating a child route with a loader from the root route', () => { context: {} location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise> cause: 'preload' | 'enter' | 'stay' route: Route }>() @@ -416,7 +420,7 @@ test('when creating a child route with a loader from the root route with context context: { userId: string } location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise> cause: 'preload' | 'enter' | 'stay' route: Route }>() @@ -549,7 +553,7 @@ test('when creating a child route with params, search and loader from the root r context: {} location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise> cause: 'preload' | 'enter' | 'stay' route: Route }> @@ -574,7 +578,7 @@ test('when creating a child route with params, search, loader and loaderDeps fro context: {} location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise> cause: 'preload' | 'enter' | 'stay' route: Route }>(), @@ -598,7 +602,7 @@ test('when creating a child route with params, search, loader and loaderDeps fro context: { userId: string } location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise> cause: 'preload' | 'enter' | 'stay' route: Route }>(), @@ -701,7 +705,7 @@ test('when creating a child route with params, search with routeContext, beforeL context: { userId: string; env: string; readonly permission: 'view' } location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise> cause: 'preload' | 'enter' | 'stay' route: Route }>() @@ -1030,7 +1034,9 @@ test('when creating a child route with routeContext, beforeLoad, search, params, } location: ParsedLocation navigate: (opts: NavigateOptions) => Promise - parentMatchPromise?: Promise + parentMatchPromise?: Promise< + MakeRouteMatchFromRoute + > cause: 'preload' | 'enter' | 'stay' route: Route }>(),