Skip to content

Commit 205336d

Browse files
committed
hold transitions through call server
1 parent 7e455b1 commit 205336d

File tree

1 file changed

+93
-84
lines changed

1 file changed

+93
-84
lines changed

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

Lines changed: 93 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import type {
1616
RSCPayload,
1717
RSCRouteManifest,
1818
RSCRenderPayload,
19+
RSCRedirectPayload,
1920
} from "./server.rsc";
2021
import type {
2122
DataStrategyFunction,
@@ -63,16 +64,20 @@ type WindowWithRouterGlobals = Window &
6364
__routerActionID: number;
6465
};
6566

67+
const startTransition = React.startTransition as (
68+
cb: () => Promise<void> | void,
69+
) => void;
70+
6671
const patchRoutes = (...args: Parameters<DataRouter["patchRoutes"]>) => {
67-
React.startTransition(() => {
72+
startTransition(() => {
6873
(window as WindowWithRouterGlobals).__reactRouterDataRouter.patchRoutes(
6974
...args,
7075
);
7176
});
7277
};
7378

7479
const navigate = (...args: Parameters<DataRouter["navigate"]>) => {
75-
React.startTransition(() => {
80+
startTransition(() => {
7681
(window as WindowWithRouterGlobals).__reactRouterDataRouter.navigate(
7782
...args,
7883
);
@@ -84,7 +89,7 @@ const internalSetStateDoNotUseOrYouWillBreakYourApp = (
8489
DataRouter["_internalSetStateDoNotUseOrYouWillBreakYourApp"]
8590
>
8691
) => {
87-
React.startTransition(() => {
92+
startTransition(() => {
8893
(
8994
window as WindowWithRouterGlobals
9095
).__reactRouterDataRouter._internalSetStateDoNotUseOrYouWillBreakYourApp(
@@ -96,7 +101,7 @@ const internalSetStateDoNotUseOrYouWillBreakYourApp = (
96101
const internalSetRoutes = (
97102
...args: Parameters<DataRouter["_internalSetRoutes"]>
98103
) => {
99-
React.startTransition(() => {
104+
startTransition(() => {
100105
(
101106
window as WindowWithRouterGlobals
102107
).__reactRouterDataRouter._internalSetRoutes(...args);
@@ -153,105 +158,109 @@ export function createCallServer({
153158
const globalVar = window as WindowWithRouterGlobals;
154159

155160
let landedActionId = 0;
156-
return async (id: string, args: unknown[]) => {
161+
return async function callServer(id: string, args: unknown[]) {
157162
let actionId = (globalVar.__routerActionID =
158163
(globalVar.__routerActionID ??= 0) + 1);
159164

160165
const temporaryReferences = createTemporaryReferenceSet();
161-
const payloadPromise = fetchImplementation(
166+
const encoded = await encodeReply(args, { temporaryReferences });
167+
168+
const response = await fetchImplementation(
162169
new Request(location.href, {
163-
body: await encodeReply(args, { temporaryReferences }),
170+
body: encoded,
164171
method: "POST",
165172
headers: {
166173
Accept: "text/x-component",
167174
"rsc-action-id": id,
168175
},
169176
}),
170-
).then((response) => {
171-
if (!response.body) {
172-
throw new Error("No response body");
173-
}
174-
return createFromReadableStream(response.body, {
175-
temporaryReferences,
176-
}) as Promise<RSCPayload>;
177-
});
177+
);
178178

179-
(React.startTransition as (cb: () => void | Promise<void>) => void)(() =>
180-
Promise.resolve(payloadPromise)
181-
.then(async (payload) => {
182-
if (payload.type === "redirect") {
183-
if (payload.reload || isExternalLocation(payload.location)) {
184-
window.location.href = payload.location;
185-
return;
186-
}
187-
188-
React.startTransition(() => {
189-
navigate(payload.location, {
190-
replace: payload.replace,
191-
});
192-
});
193-
return;
194-
}
179+
if (!response.body) {
180+
throw new Error("No response body");
181+
}
195182

196-
if (payload.type !== "action") {
197-
throw new Error("Unexpected payload type");
183+
const payload = (await Promise.resolve(
184+
createFromReadableStream(response.body, {
185+
temporaryReferences,
186+
}),
187+
)) as RSCPayload;
188+
189+
let redirect: RSCRedirectPayload | undefined;
190+
let rerender: RSCRenderPayload | undefined;
191+
let actionResult: unknown;
192+
193+
switch (payload.type) {
194+
case "action": {
195+
actionResult = payload.actionResult;
196+
let rerenderPayload = await payload.rerender;
197+
switch (rerenderPayload?.type) {
198+
case "redirect": {
199+
redirect = rerenderPayload;
200+
break;
198201
}
199-
200-
const rerender = await payload.rerender;
201-
if (
202-
rerender &&
203-
landedActionId < actionId &&
204-
globalVar.__routerActionID <= actionId
205-
) {
206-
if (rerender.type === "redirect") {
207-
if (rerender.reload || isExternalLocation(rerender.location)) {
208-
window.location.href = rerender.location;
209-
return;
210-
}
211-
React.startTransition(() => {
212-
navigate(rerender.location, {
213-
replace: rerender.replace,
214-
});
215-
});
216-
return;
217-
}
218-
219-
let lastMatch: RSCRouteManifest | undefined;
220-
for (const match of rerender.matches) {
221-
patchRoutes(
222-
lastMatch?.id ?? null,
223-
[createRouteFromServerManifest(match)],
224-
true,
225-
);
226-
lastMatch = match;
227-
}
228-
229-
internalSetStateDoNotUseOrYouWillBreakYourApp({
230-
loaderData: Object.assign(
231-
{},
232-
globalVar.__reactRouterDataRouter.state.loaderData,
233-
rerender.loaderData,
234-
),
235-
errors: rerender.errors
236-
? Object.assign(
237-
{},
238-
globalVar.__reactRouterDataRouter.state.errors,
239-
rerender.errors,
240-
)
241-
: null,
242-
});
202+
case "render": {
203+
rerender = rerenderPayload;
243204
}
244-
})
245-
.catch(() => {}),
246-
);
205+
}
206+
break;
207+
}
208+
case "redirect": {
209+
actionResult = payload.actionResult;
210+
redirect = payload;
211+
break;
212+
}
213+
case "render": {
214+
rerender = payload;
215+
break;
216+
}
217+
}
247218

248-
return payloadPromise.then((payload) => {
249-
if (payload.type !== "action" && payload.type !== "redirect") {
250-
throw new Error("Unexpected payload type");
219+
startTransition(() => {
220+
if (redirect) {
221+
if (redirect.reload || isExternalLocation(redirect.location)) {
222+
window.location.href = redirect.location;
223+
return;
224+
}
225+
226+
navigate(redirect.location, {
227+
replace: redirect.replace,
228+
});
229+
return;
251230
}
231+
if (
232+
rerender &&
233+
landedActionId < actionId &&
234+
globalVar.__routerActionID <= actionId
235+
) {
236+
let lastMatch: RSCRouteManifest | undefined;
237+
for (const match of rerender.matches) {
238+
patchRoutes(
239+
lastMatch?.id ?? null,
240+
[createRouteFromServerManifest(match)],
241+
true,
242+
);
243+
lastMatch = match;
244+
}
252245

253-
return payload.actionResult;
246+
internalSetStateDoNotUseOrYouWillBreakYourApp({
247+
loaderData: Object.assign(
248+
{},
249+
globalVar.__reactRouterDataRouter.state.loaderData,
250+
rerender.loaderData,
251+
),
252+
errors: rerender.errors
253+
? Object.assign(
254+
{},
255+
globalVar.__reactRouterDataRouter.state.errors,
256+
rerender.errors,
257+
)
258+
: null,
259+
});
260+
}
254261
});
262+
263+
return actionResult;
255264
};
256265
}
257266

0 commit comments

Comments
 (0)