Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fizz] Expose a method to abort a pending request #21027

Merged
merged 5 commits into from Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion fixtures/fizz-ssr-browser/index.html
Expand Up @@ -20,7 +20,10 @@ <h1>Fizz Example</h1>
<script src="../../build/node_modules/react-dom/umd/react-dom-unstable-fizz.browser.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
<script type="text/babel">
let {stream} = ReactDOMFizzServer.renderToReadableStream(<body>Success</body>);
let controller = new AbortController();
let stream = ReactDOMFizzServer.renderToReadableStream(<body>Success</body>, {
signal: controller.signal,
});
let response = new Response(stream, {
headers: {'Content-Type': 'text/html'},
});
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -36,6 +36,7 @@
"@babel/preset-react": "^7.10.4",
"@babel/traverse": "^7.11.0",
"@mattiasbuelens/web-streams-polyfill": "^0.3.2",
"abort-controller": "^3.0.0",
"art": "0.10.1",
"babel-eslint": "^10.0.3",
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
Expand Down
Expand Up @@ -12,6 +12,7 @@
// Polyfills for test environment
global.ReadableStream = require('@mattiasbuelens/web-streams-polyfill/ponyfill/es6').ReadableStream;
global.TextEncoder = require('util').TextEncoder;
global.AbortController = require('abort-controller');

let React;
let ReactDOMFizzServer;
Expand Down Expand Up @@ -50,7 +51,7 @@ describe('ReactDOMFizzServer', () => {

// @gate experimental
it('should call renderToReadableStream', async () => {
const {stream} = ReactDOMFizzServer.renderToReadableStream(
const stream = ReactDOMFizzServer.renderToReadableStream(
<div>hello world</div>,
);
const result = await readResult(stream);
Expand All @@ -59,7 +60,7 @@ describe('ReactDOMFizzServer', () => {

// @gate experimental
it('should error the stream when an error is thrown at the root', async () => {
const {stream} = ReactDOMFizzServer.renderToReadableStream(
const stream = ReactDOMFizzServer.renderToReadableStream(
<div>
<Throw />
</div>,
Expand All @@ -78,7 +79,7 @@ describe('ReactDOMFizzServer', () => {

// @gate experimental
it('should error the stream when an error is thrown inside a fallback', async () => {
const {stream} = ReactDOMFizzServer.renderToReadableStream(
const stream = ReactDOMFizzServer.renderToReadableStream(
<div>
<Suspense fallback={<Throw />}>
<InfiniteSuspend />
Expand All @@ -99,7 +100,7 @@ describe('ReactDOMFizzServer', () => {

// @gate experimental
it('should not error the stream when an error is thrown inside suspense boundary', async () => {
const {stream} = ReactDOMFizzServer.renderToReadableStream(
const stream = ReactDOMFizzServer.renderToReadableStream(
<div>
<Suspense fallback={<div>Loading</div>}>
<Throw />
Expand All @@ -113,15 +114,17 @@ describe('ReactDOMFizzServer', () => {

// @gate experimental
it('should be able to complete by aborting even if the promise never resolves', async () => {
const {abort, stream} = ReactDOMFizzServer.renderToReadableStream(
const controller = new AbortController();
const stream = ReactDOMFizzServer.renderToReadableStream(
<div>
<Suspense fallback={<div>Loading</div>}>
<InfiniteSuspend />
</Suspense>
</div>,
{signal: controller.signal},
);

abort();
controller.abort();

const result = await readResult(stream);
expect(result).toContain('Loading');
Expand Down
21 changes: 10 additions & 11 deletions packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
Expand Up @@ -16,14 +16,19 @@ import {
abort,
} from 'react-server/src/ReactFizzServer';

type Controls = {
stream: ReadableStream,
abort(): void,
type Options = {
signal?: AbortSignal,
};

function renderToReadableStream(children: ReactNodeList): Controls {
function renderToReadableStream(
children: ReactNodeList,
options?: Options,
): ReadableStream {
let request;
const stream = new ReadableStream({
if (options && options.signal) {
options.signal.addEventListener('abort', () => abort(request));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't ever need to be cleaned up, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I would assume the owner would drop the signal when they're done but we might as well clean it up to be sure.

}
return new ReadableStream({
start(controller) {
request = createRequest(children, controller);
startWork(request);
Expand All @@ -33,12 +38,6 @@ function renderToReadableStream(children: ReactNodeList): Controls {
},
cancel(reason) {},
});
return {
stream,
abort() {
abort(request);
},
};
}

export {renderToReadableStream};