Skip to content

Commit a3fc04b

Browse files
authored
fix(react-router): fix context parameter in createRoutesStub for non-middleware users (#13946)
* Fixx context parameter in createRoutesStub for non-middleware users * Fix type and test
1 parent d47e692 commit a3fc04b

File tree

3 files changed

+101
-10
lines changed

3 files changed

+101
-10
lines changed

.changeset/strong-parrots-jog.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Fix a regression in `createRoutesStub` introduced with the middleware feature.
6+
7+
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.
8+
9+
We've reverted this change and re-implemented it in such a way that both sets of users can leverage it.
10+
11+
```tsx
12+
// If you have not opted into middleware, the old API should work again
13+
let context: AppLoadContext = {
14+
/*...*/
15+
};
16+
let Stub = createRoutesStub(routes, context);
17+
18+
// If you have opted into middleware, you should now pass an instantiated `unstable_routerContextProvider` instead of a `getContext` factory function.
19+
let context = new unstable_RouterContextProvider();
20+
context.set(SomeContext, someValue);
21+
let Stub = createRoutesStub(routes, context);
22+
```
23+
24+
⚠️ This may be a breaking bug for if you have adopted the unstable Middleware feature and are using `createRoutesStub` with the updated API.

packages/react-router/__tests__/dom/stub-test.tsx

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import {
1212
type LoaderFunctionArgs,
1313
useRouteError,
1414
} from "../../index";
15-
import { unstable_createContext } from "../../lib/router/utils";
15+
import {
16+
unstable_RouterContextProvider,
17+
unstable_createContext,
18+
} from "../../lib/router/utils";
1619

1720
test("renders a route", () => {
1821
let RoutesStub = createRoutesStub([
@@ -236,7 +239,50 @@ test("can pass a predefined loader", () => {
236239
]);
237240
});
238241

239-
test("can pass context values", async () => {
242+
test("can pass context values (w/o middleware)", async () => {
243+
let RoutesStub = createRoutesStub(
244+
[
245+
{
246+
path: "/",
247+
HydrateFallback: () => null,
248+
Component() {
249+
let data = useLoaderData() as string;
250+
return (
251+
<div>
252+
<pre data-testid="root">Context: {data}</pre>
253+
<Outlet />
254+
</div>
255+
);
256+
},
257+
loader({ context }) {
258+
return context.message;
259+
},
260+
children: [
261+
{
262+
path: "hello",
263+
Component() {
264+
let data = useLoaderData() as string;
265+
return <pre data-testid="hello">Context: {data}</pre>;
266+
},
267+
loader({ context }) {
268+
return context.message;
269+
},
270+
},
271+
],
272+
},
273+
],
274+
{ message: "hello" }
275+
);
276+
277+
render(<RoutesStub initialEntries={["/hello"]} />);
278+
279+
expect(await screen.findByTestId("root")).toHaveTextContent(/Context: hello/);
280+
expect(await screen.findByTestId("hello")).toHaveTextContent(
281+
/Context: hello/
282+
);
283+
});
284+
285+
test("can pass context values (w/middleware)", async () => {
240286
let helloContext = unstable_createContext();
241287
let RoutesStub = createRoutesStub(
242288
[
@@ -269,10 +315,15 @@ test("can pass context values", async () => {
269315
],
270316
},
271317
],
272-
() => new Map([[helloContext, "hello"]])
318+
new unstable_RouterContextProvider(new Map([[helloContext, "hello"]]))
273319
);
274320

275-
render(<RoutesStub initialEntries={["/hello"]} />);
321+
render(
322+
<RoutesStub
323+
future={{ unstable_middleware: true }}
324+
initialEntries={["/hello"]}
325+
/>
326+
);
276327

277328
expect(await screen.findByTestId("root")).toHaveTextContent(/Context: hello/);
278329
expect(await screen.findByTestId("hello")).toHaveTextContent(

packages/react-router/lib/dom/ssr/routes-test-stub.tsx

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import * as React from "react";
22
import type {
33
ActionFunction,
4+
ActionFunctionArgs,
45
LoaderFunction,
5-
unstable_InitialContext,
6+
LoaderFunctionArgs,
67
} from "../../router/utils";
78
import type {
89
DataRouteObject,
@@ -12,7 +13,12 @@ import type {
1213
import type { LinksFunction, MetaFunction, RouteModules } from "./routeModules";
1314
import type { InitialEntry } from "../../router/history";
1415
import type { HydrationState } from "../../router/router";
15-
import { convertRoutesToDataRoutes } from "../../router/utils";
16+
import {
17+
convertRoutesToDataRoutes,
18+
unstable_RouterContextProvider,
19+
} from "../../router/utils";
20+
import type { MiddlewareEnabled } from "../../types/future";
21+
import type { AppLoadContext } from "../../server-runtime/data";
1622
import type {
1723
AssetsManifest,
1824
FutureConfig,
@@ -111,7 +117,7 @@ export interface RoutesTestStubProps {
111117
*/
112118
export function createRoutesStub(
113119
routes: StubRouteObject[],
114-
unstable_getContext?: () => unstable_InitialContext
120+
_context?: AppLoadContext | unstable_RouterContextProvider
115121
) {
116122
return function RoutesTestStub({
117123
initialEntries,
@@ -147,11 +153,15 @@ export function createRoutesStub(
147153
// @ts-expect-error `StubRouteObject` is stricter about `loader`/`action`
148154
// types compared to `AgnosticRouteObject`
149155
convertRoutesToDataRoutes(routes, (r) => r),
156+
_context !== undefined
157+
? _context
158+
: future?.unstable_middleware
159+
? new unstable_RouterContextProvider()
160+
: {},
150161
frameworkContextRef.current.manifest,
151162
frameworkContextRef.current.routeModules
152163
);
153164
routerRef.current = createMemoryRouter(patched, {
154-
unstable_getContext,
155165
initialEntries,
156166
initialIndex,
157167
hydrationData,
@@ -168,6 +178,7 @@ export function createRoutesStub(
168178

169179
function processRoutes(
170180
routes: StubRouteObject[],
181+
context: unknown,
171182
manifest: AssetsManifest,
172183
routeModules: RouteModules,
173184
parentId?: string
@@ -192,8 +203,12 @@ function processRoutes(
192203
ErrorBoundary: route.ErrorBoundary
193204
? withErrorBoundaryProps(route.ErrorBoundary)
194205
: undefined,
195-
action: route.action,
196-
loader: route.loader,
206+
action: route.action
207+
? (args: ActionFunctionArgs) => route.action!({ ...args, context })
208+
: undefined,
209+
loader: route.loader
210+
? (args: LoaderFunctionArgs) => route.loader!({ ...args, context })
211+
: undefined,
197212
handle: route.handle,
198213
shouldRevalidate: route.shouldRevalidate,
199214
};
@@ -235,6 +250,7 @@ function processRoutes(
235250
if (route.children) {
236251
newRoute.children = processRoutes(
237252
route.children,
253+
context,
238254
manifest,
239255
routeModules,
240256
newRoute.id

0 commit comments

Comments
 (0)