Skip to content

Commit 2eab3f2

Browse files
authored
feat: add fetch override to createCallServer and RSCHydratedRouter (#13839)
1 parent f400e0d commit 2eab3f2

File tree

1 file changed

+43
-20
lines changed

1 file changed

+43
-20
lines changed

packages/react-router/lib/rsc/browser.tsx

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,27 @@ declare global {
5757
export function createCallServer({
5858
decode,
5959
encodeAction,
60+
fetch: fetchImplementation = fetch,
6061
}: {
6162
decode: DecodeServerResponseFunction;
6263
encodeAction: EncodeActionFunction;
64+
fetch?: (request: Request) => Promise<Response>;
6365
}) {
6466
let landedActionId = 0;
6567
return async (id: string, args: unknown[]) => {
6668
let actionId = (window.__routerActionID =
6769
(window.__routerActionID ??= 0) + 1);
6870

69-
const response = await fetch(location.href, {
70-
body: await encodeAction(args),
71-
method: "POST",
72-
headers: {
73-
Accept: "text/x-component",
74-
"rsc-action-id": id,
75-
},
76-
});
71+
const response = await fetchImplementation(
72+
new Request(location.href, {
73+
body: await encodeAction(args),
74+
method: "POST",
75+
headers: {
76+
Accept: "text/x-component",
77+
"rsc-action-id": id,
78+
},
79+
})
80+
);
7781
if (!response.body) {
7882
throw new Error("No response body");
7983
}
@@ -162,11 +166,13 @@ export function createCallServer({
162166
}
163167

164168
function createRouterFromPayload({
169+
fetchImplementation,
165170
decode,
166171
payload,
167172
}: {
168173
payload: ServerPayload;
169174
decode: DecodeServerResponseFunction;
175+
fetchImplementation: (request: Request) => Promise<Response>;
170176
}) {
171177
if (window.__router) return window.__router;
172178

@@ -225,14 +231,20 @@ function createRouterFromPayload({
225231
if (discoveredPaths.has(path)) {
226232
return;
227233
}
228-
await fetchAndApplyManifestPatches([path], decode, signal);
234+
await fetchAndApplyManifestPatches(
235+
[path],
236+
decode,
237+
fetchImplementation,
238+
signal
239+
);
229240
},
230241
// FIXME: Pass `build.ssr` and `build.basename` into this function
231242
dataStrategy: getRSCSingleFetchDataStrategy(
232243
() => window.__router,
233244
true,
234245
undefined,
235-
decode
246+
decode,
247+
fetchImplementation
236248
),
237249
});
238250

@@ -261,7 +273,8 @@ export function getRSCSingleFetchDataStrategy(
261273
getRouter: () => DataRouter,
262274
ssr: boolean,
263275
basename: string | undefined,
264-
decode: DecodeServerResponseFunction
276+
decode: DecodeServerResponseFunction,
277+
fetchImplementation: (request: Request) => Promise<Response>
265278
): DataStrategyFunction {
266279
// TODO: Clean this up with a shared type
267280
type RSCDataRouteMatch = DataRouteMatch & {
@@ -290,7 +303,7 @@ export function getRSCSingleFetchDataStrategy(
290303
};
291304
},
292305
// pass map into fetchAndDecode so it can add payloads
293-
getFetchAndDecodeViaRSC(decode),
306+
getFetchAndDecodeViaRSC(decode, fetchImplementation),
294307
ssr,
295308
basename,
296309
// If the route has a component but we don't have an element, we need to hit
@@ -344,7 +357,8 @@ export function getRSCSingleFetchDataStrategy(
344357
}
345358

346359
function getFetchAndDecodeViaRSC(
347-
decode: DecodeServerResponseFunction
360+
decode: DecodeServerResponseFunction,
361+
fetchImplementation: (request: Request) => Promise<Response>
348362
): FetchAndDecodeFunction {
349363
return async (
350364
args: DataStrategyFunctionArgs<unknown>,
@@ -360,7 +374,9 @@ function getFetchAndDecodeViaRSC(
360374
}
361375
}
362376

363-
let res = await fetch(url, await createRequestInit(request));
377+
let res = await fetchImplementation(
378+
new Request(url, await createRequestInit(request))
379+
);
364380

365381
// If this 404'd without hitting the running server (most likely in a
366382
// pre-rendered app using a CDN), then bubble a standard 404 ErrorResponse
@@ -426,19 +442,25 @@ function getFetchAndDecodeViaRSC(
426442

427443
export function RSCHydratedRouter({
428444
decode,
445+
fetch: fetchImplementation = fetch,
429446
payload,
430447
routeDiscovery = "eager",
431448
}: {
432449
decode: DecodeServerResponseFunction;
450+
fetch?: (request: Request) => Promise<Response>;
433451
payload: ServerPayload;
434452
routeDiscovery?: "eager" | "lazy";
435453
}) {
436454
if (payload.type !== "render") throw new Error("Invalid payload type");
437455

438456
let router = React.useMemo(
439-
() => createRouterFromPayload({ decode, payload }),
440-
// eslint-disable-next-line react-hooks/exhaustive-deps
441-
[]
457+
() =>
458+
createRouterFromPayload({
459+
decode,
460+
payload,
461+
fetchImplementation,
462+
}),
463+
[decode, payload, fetchImplementation]
442464
);
443465

444466
React.useLayoutEffect(() => {
@@ -510,7 +532,7 @@ export function RSCHydratedRouter({
510532
}
511533

512534
try {
513-
await fetchAndApplyManifestPatches(paths, decode);
535+
await fetchAndApplyManifestPatches(paths, decode, fetchImplementation);
514536
} catch (e) {
515537
console.error("Failed to fetch manifest patches", e);
516538
}
@@ -529,7 +551,7 @@ export function RSCHydratedRouter({
529551
attributes: true,
530552
attributeFilter: ["data-discover", "href", "action"],
531553
});
532-
}, [routeDiscovery, decode]);
554+
}, [routeDiscovery, decode, fetchImplementation]);
533555

534556
const frameworkContext: FrameworkContextObject = {
535557
future: {
@@ -704,6 +726,7 @@ const URL_LIMIT = 7680;
704726
async function fetchAndApplyManifestPatches(
705727
paths: string[],
706728
decode: DecodeServerResponseFunction,
729+
fetchImplementation: (request: Request) => Promise<Response>,
707730
signal?: AbortSignal
708731
) {
709732
let basename = (window.__router.basename ?? "").replace(/^\/|\/$/g, "");
@@ -718,7 +741,7 @@ async function fetchAndApplyManifestPatches(
718741
return;
719742
}
720743

721-
let response = await fetch(url, { signal });
744+
let response = await fetchImplementation(new Request(url, { signal }));
722745
if (!response.body || response.status < 200 || response.status >= 300) {
723746
throw new Error("Unable to fetch new route matches from the server");
724747
}

0 commit comments

Comments
 (0)