diff --git a/.changeset/strong-parrots-jog.md b/.changeset/strong-parrots-jog.md
new file mode 100644
index 0000000000..0bfe51abea
--- /dev/null
+++ b/.changeset/strong-parrots-jog.md
@@ -0,0 +1,24 @@
+---
+"react-router": patch
+---
+
+Fix a regression in `createRoutesStub` introduced with the middleware feature.
+
+As part of that work we altered the signature to align with the new middleware APIs without making it backwards compatible with the prior `AppLoadContext` API. This permitted `createRoutesStub` to work if you were opting into middleware and the updated `context` typings, but broke `createRoutesStub` for users not yet opting into middleware.
+
+We've reverted this change and re-implemented it in such a way that both sets of users can leverage it.
+
+```tsx
+// If you have not opted into middleware, the old API should work again
+let context: AppLoadContext = {
+ /*...*/
+};
+let Stub = createRoutesStub(routes, context);
+
+// If you have opted into middleware, you should now pass an instantiated `unstable_routerContextProvider` instead of a `getContext` factory function.
+let context = new unstable_RouterContextProvider();
+context.set(SomeContext, someValue);
+let Stub = createRoutesStub(routes, context);
+```
+
+⚠️ This may be a breaking bug for if you have adopted the unstable Middleware feature and are using `createRoutesStub` with the updated API.
diff --git a/packages/react-router/__tests__/dom/stub-test.tsx b/packages/react-router/__tests__/dom/stub-test.tsx
index edb2098691..821f71548d 100644
--- a/packages/react-router/__tests__/dom/stub-test.tsx
+++ b/packages/react-router/__tests__/dom/stub-test.tsx
@@ -12,7 +12,10 @@ import {
type LoaderFunctionArgs,
useRouteError,
} from "../../index";
-import { unstable_createContext } from "../../lib/router/utils";
+import {
+ unstable_RouterContextProvider,
+ unstable_createContext,
+} from "../../lib/router/utils";
test("renders a route", () => {
let RoutesStub = createRoutesStub([
@@ -236,7 +239,50 @@ test("can pass a predefined loader", () => {
]);
});
-test("can pass context values", async () => {
+test("can pass context values (w/o middleware)", async () => {
+ let RoutesStub = createRoutesStub(
+ [
+ {
+ path: "/",
+ HydrateFallback: () => null,
+ Component() {
+ let data = useLoaderData() as string;
+ return (
+
+ );
+ },
+ loader({ context }) {
+ return context.message;
+ },
+ children: [
+ {
+ path: "hello",
+ Component() {
+ let data = useLoaderData() as string;
+ return Context: {data}
;
+ },
+ loader({ context }) {
+ return context.message;
+ },
+ },
+ ],
+ },
+ ],
+ { message: "hello" }
+ );
+
+ render();
+
+ expect(await screen.findByTestId("root")).toHaveTextContent(/Context: hello/);
+ expect(await screen.findByTestId("hello")).toHaveTextContent(
+ /Context: hello/
+ );
+});
+
+test("can pass context values (w/middleware)", async () => {
let helloContext = unstable_createContext();
let RoutesStub = createRoutesStub(
[
@@ -269,10 +315,15 @@ test("can pass context values", async () => {
],
},
],
- () => new Map([[helloContext, "hello"]])
+ new unstable_RouterContextProvider(new Map([[helloContext, "hello"]]))
);
- render();
+ render(
+
+ );
expect(await screen.findByTestId("root")).toHaveTextContent(/Context: hello/);
expect(await screen.findByTestId("hello")).toHaveTextContent(
diff --git a/packages/react-router/lib/dom/ssr/routes-test-stub.tsx b/packages/react-router/lib/dom/ssr/routes-test-stub.tsx
index f339b2879b..ce4376ea7e 100644
--- a/packages/react-router/lib/dom/ssr/routes-test-stub.tsx
+++ b/packages/react-router/lib/dom/ssr/routes-test-stub.tsx
@@ -1,8 +1,9 @@
import * as React from "react";
import type {
ActionFunction,
+ ActionFunctionArgs,
LoaderFunction,
- unstable_InitialContext,
+ LoaderFunctionArgs,
} from "../../router/utils";
import type {
DataRouteObject,
@@ -12,7 +13,12 @@ import type {
import type { LinksFunction, MetaFunction, RouteModules } from "./routeModules";
import type { InitialEntry } from "../../router/history";
import type { HydrationState } from "../../router/router";
-import { convertRoutesToDataRoutes } from "../../router/utils";
+import {
+ convertRoutesToDataRoutes,
+ unstable_RouterContextProvider,
+} from "../../router/utils";
+import type { MiddlewareEnabled } from "../../types/future";
+import type { AppLoadContext } from "../../server-runtime/data";
import type {
AssetsManifest,
FutureConfig,
@@ -111,7 +117,7 @@ export interface RoutesTestStubProps {
*/
export function createRoutesStub(
routes: StubRouteObject[],
- unstable_getContext?: () => unstable_InitialContext
+ _context?: AppLoadContext | unstable_RouterContextProvider
) {
return function RoutesTestStub({
initialEntries,
@@ -147,11 +153,15 @@ export function createRoutesStub(
// @ts-expect-error `StubRouteObject` is stricter about `loader`/`action`
// types compared to `AgnosticRouteObject`
convertRoutesToDataRoutes(routes, (r) => r),
+ _context !== undefined
+ ? _context
+ : future?.unstable_middleware
+ ? new unstable_RouterContextProvider()
+ : {},
frameworkContextRef.current.manifest,
frameworkContextRef.current.routeModules
);
routerRef.current = createMemoryRouter(patched, {
- unstable_getContext,
initialEntries,
initialIndex,
hydrationData,
@@ -168,6 +178,7 @@ export function createRoutesStub(
function processRoutes(
routes: StubRouteObject[],
+ context: unknown,
manifest: AssetsManifest,
routeModules: RouteModules,
parentId?: string
@@ -192,8 +203,12 @@ function processRoutes(
ErrorBoundary: route.ErrorBoundary
? withErrorBoundaryProps(route.ErrorBoundary)
: undefined,
- action: route.action,
- loader: route.loader,
+ action: route.action
+ ? (args: ActionFunctionArgs) => route.action!({ ...args, context })
+ : undefined,
+ loader: route.loader
+ ? (args: LoaderFunctionArgs) => route.loader!({ ...args, context })
+ : undefined,
handle: route.handle,
shouldRevalidate: route.shouldRevalidate,
};
@@ -235,6 +250,7 @@ function processRoutes(
if (route.children) {
newRoute.children = processRoutes(
route.children,
+ context,
manifest,
routeModules,
newRoute.id