Skip to content

Commit

Permalink
Remove deprecated ABORT_DELAY in favor of streamTimeout (#12478)
Browse files Browse the repository at this point in the history
  • Loading branch information
brophdawg11 authored Dec 6, 2024
1 parent 0ea8e66 commit 5213a95
Show file tree
Hide file tree
Showing 11 changed files with 60 additions and 68 deletions.
8 changes: 8 additions & 0 deletions .changeset/wild-dogs-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@react-router/dev": patch
---

Remove the leftover/unused `abortDelay` prop from `ServerRouter` and update the default `entry.server.tsx` to use the new `streamTimeout` value for Single Fetch

- The `abortDelay` functionality was removed in v7 as it was coupled to the `defer` implementation from Remix v2, but this removal of this prop was missed
- If you were still using this prop in your `entry.server` file, it's likely your app is not aborting streams as you would expect and you will need to adopt the new [`streamTimeout`](https://reactrouter.com/explanation/special-files#streamtimeout) value introduced with Single Fetch
27 changes: 27 additions & 0 deletions docs/explanation/special-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,32 @@ The `default` export of this module is a function that lets you create the respo

This module should render the markup for the current page using a `<ServerRouter>` element with the `context` and `url` for the current request. This markup will (optionally) be re-hydrated once JavaScript loads in the browser using the [client entry module][client-entry].

### `streamTimeout`

If you are [streaming] responses, you can export an optional `streamTimeout` value (in milliseconds) that will control the amount of time the server will wait for streamed promises to settle before rejecting outstanding promises them and closing the stream.

It's recommended to decouple this value from the timeout in which you abort the React renderer. You should always set the React rendering timeout to a higher value so it has time to stream down the underlying rejections from your `streamTimeout`.

```tsx lines=[1-2,13-15]
// Reject all pending promises from handler functions after 10 seconds
export const streamTimeout = 10000;

export default function handleRequest(...) {
return new Promise((resolve, reject) => {
// ...

const { pipe, abort } = renderToPipeableStream(
<ServerRouter context={routerContext} url={request.url} />,
{ /* ... */ }
);

// Abort the streaming render pass after 11 seconds soto allow the rejected
// boundaries to be flushed
setTimeout(abort, streamTimeout + 1000);
});
}
```

### `handleDataRequest`

You can export an optional `handleDataRequest` function that will allow you to modify the response of a data request. These are the requests that do not render HTML, but rather return the loader and action data to the browser once client-side hydration has occurred.
Expand Down Expand Up @@ -289,3 +315,4 @@ Note that this does not handle thrown `Response` instances from your `loader`/`a
[rendertopipeablestream]: https://react.dev/reference/react-dom/server/renderToPipeableStream
[rendertoreadablestream]: https://react.dev/reference/react-dom/server/renderToReadableStream
[node-streaming-entry-server]: https://github.com/remix-run/react-router/blob/dev/packages/react-router-dev/config/defaults/entry.server.node.tsx
[streaming]: ../how-to/suspense
10 changes: 2 additions & 8 deletions integration/error-sanitization-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,8 +497,6 @@ test.describe("Error Sanitization", () => {
import { ServerRouter, isRouteErrorResponse } from "react-router";
import { renderToPipeableStream } from "react-dom/server";
const ABORT_DELAY = 5_000;
export default function handleRequest(
request,
responseStatusCode,
Expand All @@ -508,11 +506,7 @@ test.describe("Error Sanitization", () => {
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={remixContext} url={request.url} />,
{
onShellReady() {
shellRendered = true;
Expand Down Expand Up @@ -545,7 +539,7 @@ test.describe("Error Sanitization", () => {
}
);
setTimeout(abort, ABORT_DELAY);
setTimeout(abort, 5000);
});
}
Expand Down
10 changes: 2 additions & 8 deletions integration/vite-dev-custom-entry-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ const files: Files = async ({ port }) => ({
import { ServerRouter } from "react-router";
import { renderToPipeableStream } from "react-dom/server";
const ABORT_DELAY = 5_000;
export default function handleRequest(
request: Request,
responseStatusCode: number,
Expand All @@ -25,11 +23,7 @@ const files: Files = async ({ port }) => ({
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={remixContext} url={request.url} />,
{
onShellReady() {
shellRendered = true;
Expand Down Expand Up @@ -65,7 +59,7 @@ const files: Files = async ({ port }) => ({
}
);
setTimeout(abort, ABORT_DELAY);
setTimeout(abort, 5000);
});
}
`,
Expand Down
10 changes: 2 additions & 8 deletions integration/vite-spa-mode-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,6 @@ test.describe("SPA Mode", () => {
import { ServerRouter } from "react-router";
import { renderToPipeableStream } from "react-dom/server";
const ABORT_DELAY = 5_000;
export default function handleRequest(
request: Request,
responseStatusCode: number,
Expand All @@ -322,11 +320,7 @@ test.describe("SPA Mode", () => {
const html = await new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={remixContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={remixContext} url={request.url} />,
{
onAllReady() {
shellRendered = true;
Expand Down Expand Up @@ -359,7 +353,7 @@ test.describe("SPA Mode", () => {
}
);
setTimeout(abort, ABORT_DELAY);
setTimeout(abort, 5000);
});
const shellHtml = fs
Expand Down
12 changes: 5 additions & 7 deletions packages/react-router-dev/config/defaults/entry.server.node.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { isbot } from "isbot";
import type { RenderToPipeableStreamOptions } from "react-dom/server";
import { renderToPipeableStream } from "react-dom/server";

const ABORT_DELAY = 5_000;
export const streamTimeout = 5_000;

export default function handleRequest(
request: Request,
Expand All @@ -28,11 +28,7 @@ export default function handleRequest(
: "onShellReady";

const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={routerContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={routerContext} url={request.url} />,
{
[readyOption]() {
shellRendered = true;
Expand Down Expand Up @@ -65,6 +61,8 @@ export default function handleRequest(
}
);

setTimeout(abort, ABORT_DELAY);
// Abort the rendering stream after the `streamTimeout` so it has tine to
// flush down the rejected boundaries
setTimeout(abort, streamTimeout + 1000);
});
}
1 change: 0 additions & 1 deletion packages/react-router/lib/dom/ssr/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ export interface FrameworkContextObject {
serverHandoffString?: string;
future: FutureConfig;
isSpaMode: boolean;
abortDelay?: number;
serializeError?(error: Error): SerializedError;
renderMeta?: {
didRenderScripts?: boolean;
Expand Down
3 changes: 0 additions & 3 deletions packages/react-router/lib/dom/ssr/server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { StreamTransfer } from "./single-fetch";
export interface ServerRouterProps {
context: EntryContext;
url: string | URL;
abortDelay?: number;
nonce?: string;
}

Expand All @@ -25,7 +24,6 @@ export interface ServerRouterProps {
export function ServerRouter({
context,
url,
abortDelay,
nonce,
}: ServerRouterProps): ReactElement {
if (typeof url === "string") {
Expand Down Expand Up @@ -79,7 +77,6 @@ export function ServerRouter({
future: context.future,
isSpaMode: context.isSpaMode,
serializeError: context.serializeError,
abortDelay,
renderMeta: context.renderMeta,
}}
>
Expand Down
11 changes: 6 additions & 5 deletions packages/react-router/lib/server-runtime/single-fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -313,11 +313,12 @@ export function encodeViaTurboStream(
) {
let controller = new AbortController();
// How long are we willing to wait for all of the promises in `data` to resolve
// before timing out? We default this to 50ms shorter than the default value for
// `ABORT_DELAY` in our built-in `entry.server.tsx` so that once we reject we
// have time to flush the rejections down through React's rendering stream before `
// we call abort() on that. If the user provides their own it's up to them to
// decouple the aborting of the stream from the aborting of React's renderToPipeableStream
// before timing out? We default this to 50ms shorter than the default value
// of 5000ms we had in `ABORT_DELAY` in Remix v2 that folks may still be using
// in RR v7 so that once we reject we have time to flush the rejections down
// through React's rendering stream before we call `abort()` on that. If the
// user provides their own it's up to them to decouple the aborting of the
// stream from the aborting of React's `renderToPipeableStream`
let timeoutId = setTimeout(
() => controller.abort(new Error("Server Timeout")),
typeof streamTimeout === "number" ? streamTimeout : 4950
Expand Down
18 changes: 4 additions & 14 deletions playground/framework-express/app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { ServerRouter } from "react-router";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";

const ABORT_DELAY = 5_000;

export default function handleRequest(
request: Request,
responseStatusCode: number,
Expand Down Expand Up @@ -39,11 +37,7 @@ function handleBotRequest(
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={reactRouterContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={reactRouterContext} url={request.url} />,
{
onAllReady() {
shellRendered = true;
Expand Down Expand Up @@ -76,7 +70,7 @@ function handleBotRequest(
}
);

setTimeout(abort, ABORT_DELAY);
setTimeout(abort, 5000);
});
}

Expand All @@ -89,11 +83,7 @@ function handleBrowserRequest(
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={reactRouterContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={reactRouterContext} url={request.url} />,
{
onShellReady() {
shellRendered = true;
Expand Down Expand Up @@ -126,6 +116,6 @@ function handleBrowserRequest(
}
);

setTimeout(abort, ABORT_DELAY);
setTimeout(abort, 5000);
});
}
18 changes: 4 additions & 14 deletions playground/framework/app/entry.server.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { ServerRouter } from "react-router";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";

const ABORT_DELAY = 5_000;

export default function handleRequest(
request: Request,
responseStatusCode: number,
Expand Down Expand Up @@ -39,11 +37,7 @@ function handleBotRequest(
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={reactRouterContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={reactRouterContext} url={request.url} />,
{
onAllReady() {
shellRendered = true;
Expand Down Expand Up @@ -76,7 +70,7 @@ function handleBotRequest(
}
);

setTimeout(abort, ABORT_DELAY);
setTimeout(abort, 5000);
});
}

Expand All @@ -89,11 +83,7 @@ function handleBrowserRequest(
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<ServerRouter
context={reactRouterContext}
url={request.url}
abortDelay={ABORT_DELAY}
/>,
<ServerRouter context={reactRouterContext} url={request.url} />,
{
onShellReady() {
shellRendered = true;
Expand Down Expand Up @@ -126,6 +116,6 @@ function handleBrowserRequest(
}
);

setTimeout(abort, ABORT_DELAY);
setTimeout(abort, 5000);
});
}

0 comments on commit 5213a95

Please sign in to comment.