diff --git a/.changeset/twelve-dogs-pump.md b/.changeset/twelve-dogs-pump.md new file mode 100644 index 0000000000..d61525d917 --- /dev/null +++ b/.changeset/twelve-dogs-pump.md @@ -0,0 +1,5 @@ +--- +"react-router": patch +--- + +Fix Data Mode regression causing a 404 during initial load in when `middleware` exists without any `loader` functions diff --git a/packages/react-router/__tests__/router/context-middleware-test.tsx b/packages/react-router/__tests__/router/context-middleware-test.tsx index 9db190d373..cea82e235c 100644 --- a/packages/react-router/__tests__/router/context-middleware-test.tsx +++ b/packages/react-router/__tests__/router/context-middleware-test.tsx @@ -300,6 +300,62 @@ describe("context/middleware", () => { ]); }); + it("runs middleware on initialization even if no loaders exist", async () => { + let snapshot; + router = createRouter({ + history: createMemoryHistory(), + routes: [ + { + path: "/", + middleware: [ + async ({ context }, next) => { + await next(); + // Grab a snapshot at the end of the upwards middleware chain + snapshot = context.get(orderContext); + }, + getOrderMiddleware(orderContext, "a"), + getOrderMiddleware(orderContext, "b"), + ], + children: [ + { + index: true, + middleware: [ + getOrderMiddleware(orderContext, "c"), + getOrderMiddleware(orderContext, "d"), + ], + }, + ], + }, + ], + }); + let initPromise = new Promise((r) => { + let unsub = router.subscribe((state) => { + if (state.initialized) { + unsub(); + r(undefined); + } + }); + }); + await router.initialize(); + await initPromise; + expect(router.state).toMatchObject({ + initialized: true, + location: { pathname: "/" }, + navigation: { state: "idle" }, + errors: null, + }); + expect(snapshot).toEqual([ + "a middleware - before next()", + "b middleware - before next()", + "c middleware - before next()", + "d middleware - before next()", + "d middleware - after next()", + "c middleware - after next()", + "b middleware - after next()", + "a middleware - after next()", + ]); + }); + it("runs middleware even if no loaders exist", async () => { let snapshot; router = createRouter({ diff --git a/packages/react-router/lib/router/router.ts b/packages/react-router/lib/router/router.ts index 2c3e379345..4cd9d1df51 100644 --- a/packages/react-router/lib/router/router.ts +++ b/packages/react-router/lib/router/router.ts @@ -5744,13 +5744,22 @@ function getDataStrategyMatch( return shouldRevalidateLoader(match, unstable_shouldRevalidateArgs); }, resolve(handlerOverride) { - if ( + let { lazy, loader, middleware } = match.route; + + let callHandler = isUsingNewApi || shouldLoad || (handlerOverride && !isMutationMethod(request.method) && - (match.route.lazy || match.route.loader)) - ) { + (lazy || loader)); + + // If this match was marked `shouldLoad` due to a middleware and it + // doesn't have a `loader` to run and no `lazy` to add one, then we can + // just return undefined from the "loader" here + let isMiddlewareOnlyRoute = + middleware && middleware.length > 0 && !loader && !lazy; + + if (callHandler && !isMiddlewareOnlyRoute) { return callLoaderOrAction({ request, match,