From 8030db7e002e9003a5f8c3caadcdf42357eca430 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Fri, 19 Sep 2025 12:16:37 -0400 Subject: [PATCH 1/2] fix middleware in data routers whern used with createRoutesFromElements --- .changeset/slow-laws-do.md | 5 + ...re-test.ts => context-middleware-test.tsx} | 196 ++++++++++++------ packages/react-router/lib/components.tsx | 11 + 3 files changed, 144 insertions(+), 68 deletions(-) create mode 100644 .changeset/slow-laws-do.md rename packages/react-router/__tests__/router/{context-middleware-test.ts => context-middleware-test.tsx} (96%) diff --git a/.changeset/slow-laws-do.md b/.changeset/slow-laws-do.md new file mode 100644 index 0000000000..13ba3cfc49 --- /dev/null +++ b/.changeset/slow-laws-do.md @@ -0,0 +1,5 @@ +--- +"react-router": patch +--- + +Support `middleware` prop on `` for usage with a data router via `createRoutesFromElements` diff --git a/packages/react-router/__tests__/router/context-middleware-test.ts b/packages/react-router/__tests__/router/context-middleware-test.tsx similarity index 96% rename from packages/react-router/__tests__/router/context-middleware-test.ts rename to packages/react-router/__tests__/router/context-middleware-test.tsx index 6bae7d1a34..9db190d373 100644 --- a/packages/react-router/__tests__/router/context-middleware-test.ts +++ b/packages/react-router/__tests__/router/context-middleware-test.tsx @@ -1,3 +1,6 @@ +import * as React from "react"; + +import { Route, createRoutesFromElements } from "../../lib/components"; import { createMemoryHistory } from "../../lib/router/history"; import type { Router, StaticHandlerContext } from "../../lib/router/router"; import { @@ -12,11 +15,11 @@ import type { RouterContext, } from "../../lib/router/utils"; import { - createContext, - redirect, + ErrorResponseImpl, RouterContextProvider, + createContext, data, - ErrorResponseImpl, + redirect, } from "../../lib/router/utils"; import { cleanup } from "./utils/data-router-setup"; import { createFormData, invariant, tick } from "./utils/utils"; @@ -163,7 +166,7 @@ describe("context/middleware", () => { child: context.get(childContext), }), handle: { - middleware(context) { + middleware(context: RouterContextProvider) { context.set(parentContext, "PARENT MIDDLEWARE"); }, }, @@ -176,7 +179,7 @@ describe("context/middleware", () => { child: context.get(childContext), }), handle: { - middleware(context) { + middleware(context: RouterContextProvider) { context.set( parentContext, context.get(parentContext) + " (amended from child)", @@ -433,8 +436,11 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async ({ context }, next) => { - let results = await next(); + async (_, next) => { + let results = (await next()) as Record< + string, + DataStrategyResult + >; values.push({ ...results }); return results; }, @@ -447,8 +453,11 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - async ({ context }, next) => { - let results = await next(); + async (_, next) => { + let results = (await next()) as Record< + string, + DataStrategyResult + >; values.push({ ...results }); return results; }, @@ -493,7 +502,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - ({ context }, next) => { + ({ context }) => { context.set(parentContext, "PARENT MIDDLEWARE"); }, ], @@ -505,7 +514,7 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - ({ context }, next) => { + ({ context }) => { context.set(childContext, "CHILD MIDDLEWARE"); }, ], @@ -619,6 +628,57 @@ describe("context/middleware", () => { unsub(); }); + + it("works with createRoutesFromElements", async () => { + let context = new RouterContextProvider(); + router = createRouter({ + history: createMemoryHistory(), + getContext: () => context, + routes: createRoutesFromElements( + <> + + { + context.get(orderContext).push("parent loader"); + }} + > + { + context.get(orderContext).push("child loader"); + }} + /> + + , + ), + }); + + await router.navigate("/parent/child"); + + expect(context.get(orderContext)).toEqual([ + "a middleware - before next()", + "b middleware - before next()", + "c middleware - before next()", + "d middleware - before next()", + "parent loader", + "child loader", + "d middleware - after next()", + "c middleware - after next()", + "b middleware - after next()", + "a middleware - after next()", + ]); + }); }); describe("lazy", () => { @@ -1035,8 +1095,8 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async ({ context }, next) => { - await next(); + async () => { + // next was unused }, async ({ request, context }, next) => { if (request.method !== "GET") { @@ -1318,7 +1378,7 @@ describe("context/middleware", () => { { path: "/parent", middleware: [ - async (_, next) => { + async () => { throw redirect("/target"); }, ], @@ -1671,7 +1731,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async (_, next) => { + async () => { return new Response("test"); }, ], @@ -1704,7 +1764,7 @@ describe("context/middleware", () => { expect(isResponse(result)).toBe(true); return result; }, - async (_, next) => { + async () => { return data("not found", { status: 404 }); }, ], @@ -1738,7 +1798,7 @@ describe("context/middleware", () => { expect(isResponse(result)).toBe(true); return result; }, - async (_, next) => { + async () => { throw data("not found", { status: 404, statusText: "Not Found" }); }, ], @@ -1984,7 +2044,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - ({ context }, next) => { + ({ context }) => { context.set(parentContext, "PARENT MIDDLEWARE"); }, ], @@ -1996,7 +2056,7 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - ({ context }, next) => { + ({ context }) => { context.set(childContext, "CHILD MIDDLEWARE"); }, ], @@ -2079,7 +2139,7 @@ describe("context/middleware", () => { await next(); pushOrderContext(context, "PARENT 1 UP"); }, - async ({ context }, next) => { + async () => { throw new Error("PARENT 2"); }, ], @@ -2097,7 +2157,7 @@ describe("context/middleware", () => { pushOrderContext(context, "CHILD UP"); }, ], - loader({ context }) { + loader() { return "CHILD"; }, }, @@ -2153,7 +2213,7 @@ describe("context/middleware", () => { throw new Error("CHILD UP"); }, ], - loader({ context }) { + loader() { return "CHILD"; }, }, @@ -2192,7 +2252,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "parent start"); let res = await next(); pushOrderContext(context, "parent end"); @@ -2208,11 +2268,11 @@ describe("context/middleware", () => { path: "child", hasErrorBoundary: true, middleware: [ - async ({ request, context }, next) => { + async ({ context }) => { pushOrderContext(context, "child 1 start - throwing"); throw new Error("child 1 error"); }, - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "child 2 start"); let res = await next(); pushOrderContext(context, "child 2 end"); @@ -2263,7 +2323,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "parent start"); let res = await next(); pushOrderContext(context, "parent end"); @@ -2279,12 +2339,12 @@ describe("context/middleware", () => { path: "child", hasErrorBoundary: true, middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "child 1 start"); await next(); pushOrderContext(context, "child 1 end"); }, - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "child 2 start"); await next(); pushOrderContext(context, "child 2 end - throwing"); @@ -2340,7 +2400,7 @@ describe("context/middleware", () => { path: "/parent", hasErrorBoundary: true, middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "parent start"); let res = await next(); pushOrderContext(context, "parent end"); @@ -2355,11 +2415,11 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - async ({ request, context }, next) => { + async ({ context }) => { pushOrderContext(context, "child 1 start - throwing"); throw new Error("child 1 error"); }, - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "child 2 start"); let res = await next(); pushOrderContext(context, "child 2 end"); @@ -2410,7 +2470,7 @@ describe("context/middleware", () => { path: "/parent", hasErrorBoundary: true, middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "parent start"); let res = await next(); pushOrderContext(context, "parent end"); @@ -2425,13 +2485,13 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "child 1 start"); let res = await next(); pushOrderContext(context, "child 1 end"); return res; }, - async ({ request, context }, next) => { + async ({ context }, next) => { pushOrderContext(context, "child 2 start"); await next(); pushOrderContext(context, "child 2 end - throwing"); @@ -2482,7 +2542,7 @@ describe("context/middleware", () => { { path: "/", middleware: [ - async (_, next) => { + async () => { throw new Response("Error", { status: 401 }); }, ], @@ -2513,7 +2573,7 @@ describe("context/middleware", () => { { path: "/parent", middleware: [ - async (_, next) => { + async () => { throw redirect("/target"); }, ], @@ -2597,12 +2657,12 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async ({ context }, next) => { + async (_, next) => { let res = (await next()) as Response; res.headers.set("parent1", "yes"); return res; }, - async ({ context }, next) => { + async (_, next) => { let res = (await next()) as Response; res.headers.set("parent2", "yes"); return res; @@ -2616,18 +2676,18 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - async ({ context }, next) => { + async (_, next) => { let res = (await next()) as Response; res.headers.set("child1", "yes"); return res; }, - async ({ context }, next) => { + async (_, next) => { let res = (await next()) as Response; res.headers.set("child2", "yes"); return res; }, ], - loader({ context }) { + loader() { return new Response("CHILD"); }, }, @@ -2657,12 +2717,12 @@ describe("context/middleware", () => { path: "/parent", lazy: { middleware: async () => [ - async ({ context }, next) => { + async (_, next) => { let res = (await next()) as Response; res.headers.set("parent1", "yes"); return res; }, - async ({ context }, next) => { + async (_, next) => { let res = (await next()) as Response; res.headers.set("parent2", "yes"); return res; @@ -2678,19 +2738,19 @@ describe("context/middleware", () => { path: "child", lazy: { middleware: async () => [ - async ({ context }, next) => { + async (_, next) => { let res = (await next()) as Response; res.headers.set("child1", "yes"); return res; }, - async ({ context }, next) => { + async (_, next) => { let res = (await next()) as Response; res.headers.set("child2", "yes"); return res; }, ], }, - loader({ context }) { + loader() { return new Response("CHILD"); }, }, @@ -2748,7 +2808,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async (_, next) => { + async () => { return new Response("test"); }, ], @@ -2780,7 +2840,7 @@ describe("context/middleware", () => { expect(isResponse(result)).toBe(true); return result; }, - async (_, next) => { + async () => { return data("not found", { status: 404 }); }, ], @@ -2813,7 +2873,7 @@ describe("context/middleware", () => { expect(isResponse(result)).toBe(true); return result; }, - async (_, next) => { + async () => { throw data("not found", { status: 404 }); }, ], @@ -3023,7 +3083,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - ({ context }, next) => { + ({ context }) => { context.set(parentContext, "PARENT MIDDLEWARE"); }, ], @@ -3035,7 +3095,7 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - ({ context }, next) => { + ({ context }) => { context.set(childContext, "CHILD MIDDLEWARE"); }, ], @@ -3098,10 +3158,10 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async ({ context }, next) => { + async ({ context }) => { context.set(parentContext, "PARENT 1"); }, - async ({ context }, next) => { + async () => { throw new Error("PARENT 2"); }, ], @@ -3118,7 +3178,7 @@ describe("context/middleware", () => { return next(); }, ], - loader({ context }) { + loader() { return "CHILD"; }, }, @@ -3169,7 +3229,7 @@ describe("context/middleware", () => { throw new Error("CHILD UP"); }, ], - loader({ context }) { + loader() { return "CHILD"; }, }, @@ -3199,7 +3259,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "parent start", @@ -3221,14 +3281,14 @@ describe("context/middleware", () => { path: "child", hasErrorBoundary: true, middleware: [ - async ({ request, context }, next) => { + async ({ context }) => { context.set(orderContext, [ ...context.get(orderContext), "child 1 start - throwing", ]); throw new Error("child 1 error"); }, - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "child 2 start", @@ -3278,7 +3338,7 @@ describe("context/middleware", () => { id: "parent", path: "/parent", middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "parent start", @@ -3300,7 +3360,7 @@ describe("context/middleware", () => { path: "child", hasErrorBoundary: true, middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "child 1 start", @@ -3312,7 +3372,7 @@ describe("context/middleware", () => { ]); return res; }, - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "child 2 start", @@ -3366,7 +3426,7 @@ describe("context/middleware", () => { path: "/parent", hasErrorBoundary: true, middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "parent action start", @@ -3387,14 +3447,14 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - async ({ request, context }, next) => { + async ({ context }) => { context.set(orderContext, [ ...context.get(orderContext), "child 1 start - throwing", ]); throw new Error("child 1 action error"); }, - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "child 2 start", @@ -3446,7 +3506,7 @@ describe("context/middleware", () => { path: "/parent", hasErrorBoundary: true, middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "parent start", @@ -3467,7 +3527,7 @@ describe("context/middleware", () => { id: "child", path: "child", middleware: [ - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "child 1 start", @@ -3479,7 +3539,7 @@ describe("context/middleware", () => { ]); return res; }, - async ({ request, context }, next) => { + async ({ context }, next) => { context.set(orderContext, [ ...context.get(orderContext), "child 2 start", @@ -3531,7 +3591,7 @@ describe("context/middleware", () => { { path: "/parent", middleware: [ - async (_, next) => { + async () => { throw redirect("/target"); }, ], diff --git a/packages/react-router/lib/components.tsx b/packages/react-router/lib/components.tsx index 397ce2cb9e..c903ca829a 100644 --- a/packages/react-router/lib/components.tsx +++ b/packages/react-router/lib/components.tsx @@ -841,6 +841,11 @@ export interface PathRouteProps { * See [`lazy`](../../start/data/route-object#lazy). */ lazy?: LazyRouteFunction; + /** + * The route middleware. + * See [`middleware`](../../start/data/route-object#middleware). + */ + middleware?: NonIndexRouteObject["middleware"]; /** * The route loader. * See [`loader`](../../start/data/route-object#loader). @@ -929,6 +934,11 @@ export interface IndexRouteProps { * See [`lazy`](../../start/data/route-object#lazy). */ lazy?: LazyRouteFunction; + /** + * The route middleware. + * See [`middleware`](../../start/data/route-object#middleware). + */ + middleware?: IndexRouteObject["middleware"]; /** * The route loader. * See [`loader`](../../start/data/route-object#loader). @@ -1578,6 +1588,7 @@ export function createRoutesFromChildren( Component: element.props.Component, index: element.props.index, path: element.props.path, + middleware: element.props.middleware, loader: element.props.loader, action: element.props.action, hydrateFallbackElement: element.props.hydrateFallbackElement, From 63350608a7ff79993d34bbc852d6fa9837992889 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Fri, 19 Sep 2025 12:23:51 -0400 Subject: [PATCH 2/2] Fix test --- .../__tests__/createRoutesFromChildren-test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/react-router/__tests__/createRoutesFromChildren-test.tsx b/packages/react-router/__tests__/createRoutesFromChildren-test.tsx index 8d66558dd7..f42585e813 100644 --- a/packages/react-router/__tests__/createRoutesFromChildren-test.tsx +++ b/packages/react-router/__tests__/createRoutesFromChildren-test.tsx @@ -40,6 +40,7 @@ describe("creating routes from JSX", () => { "index": undefined, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": "home", "shouldRevalidate": undefined, }, @@ -60,6 +61,7 @@ describe("creating routes from JSX", () => { "index": undefined, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": "about", "shouldRevalidate": undefined, }, @@ -87,6 +89,7 @@ describe("creating routes from JSX", () => { "index": true, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": undefined, "shouldRevalidate": undefined, }, @@ -107,6 +110,7 @@ describe("creating routes from JSX", () => { "index": undefined, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": ":id", "shouldRevalidate": undefined, }, @@ -120,6 +124,7 @@ describe("creating routes from JSX", () => { "index": undefined, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": "users", "shouldRevalidate": undefined, }, @@ -133,6 +138,7 @@ describe("creating routes from JSX", () => { "index": undefined, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": "/", "shouldRevalidate": undefined, }, @@ -186,6 +192,7 @@ describe("creating routes from JSX", () => { "index": undefined, "lazy": undefined, "loader": [Function], + "middleware": undefined, "path": "home", "shouldRevalidate": [Function], }, @@ -213,6 +220,7 @@ describe("creating routes from JSX", () => { "index": true, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": undefined, "shouldRevalidate": undefined, }, @@ -226,6 +234,7 @@ describe("creating routes from JSX", () => { "index": undefined, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": "users", "shouldRevalidate": undefined, }, @@ -241,6 +250,7 @@ describe("creating routes from JSX", () => { "index": undefined, "lazy": undefined, "loader": undefined, + "middleware": undefined, "path": "/", "shouldRevalidate": undefined, },