Skip to content

Commit 53284cd

Browse files
committed
[Flight][Fizz] schedule work async
While most builds of Flight and Fizz schedule work in new tasks some do execute work synchronously. While this is necessary for legacy APIs like renderToString for modern APIs there really isn't a great reason to do this synchronously. This change updates all non-legacy uses to be async using the best availalble scheduler at runtime
1 parent adbec0c commit 53284cd

16 files changed

+1058
-636
lines changed

packages/react-dom/src/__tests__/ReactClassComponentPropResolutionFizz-test.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
'use strict';
1111

1212
import {insertNodesAndExecuteScripts} from '../test-utils/FizzTestUtils';
13+
import {patchMessageChannel} from '../../../../scripts/jest/patchMessageChannel';
1314

1415
// Polyfills for test environment
1516
global.ReadableStream =
@@ -21,12 +22,15 @@ let ReactDOMServer;
2122
let Scheduler;
2223
let assertLog;
2324
let container;
25+
let act;
2426

2527
describe('ReactClassComponentPropResolutionFizz', () => {
2628
beforeEach(() => {
2729
jest.resetModules();
28-
React = require('react');
2930
Scheduler = require('scheduler');
31+
patchMessageChannel(Scheduler);
32+
act = require('internal-test-utils').act;
33+
React = require('react');
3034
ReactDOMServer = require('react-dom/server.browser');
3135
assertLog = require('internal-test-utils').assertLog;
3236
container = document.createElement('div');
@@ -80,11 +84,16 @@ describe('ReactClassComponentPropResolutionFizz', () => {
8084
};
8185

8286
// `ref` should never appear as a prop. `default` always should.
83-
const ref = React.createRef();
84-
const stream = await ReactDOMServer.renderToReadableStream(
85-
<Component text="Yay" ref={ref} />,
86-
);
87+
let streamPromise;
88+
await act(() => {
89+
const ref = React.createRef();
90+
streamPromise = ReactDOMServer.renderToReadableStream(
91+
<Component text="Yay" ref={ref} />,
92+
);
93+
});
94+
const stream = await streamPromise;
8795
await readIntoContainer(stream);
96+
8897
assertLog([
8998
'constructor: text, default',
9099
'componentWillMount: text, default',

packages/react-dom/src/__tests__/ReactDOMFizzDeferredValue-test.js

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
insertNodesAndExecuteScripts,
1414
getVisibleChildren,
1515
} from '../test-utils/FizzTestUtils';
16+
import {patchMessageChannel} from '../../../../scripts/jest/patchMessageChannel';
1617

1718
// Polyfills for test environment
1819
global.ReadableStream =
@@ -33,8 +34,9 @@ let Suspense;
3334
describe('ReactDOMFizzForm', () => {
3435
beforeEach(() => {
3536
jest.resetModules();
36-
React = require('react');
3737
Scheduler = require('scheduler');
38+
patchMessageChannel(Scheduler);
39+
React = require('react');
3840
ReactDOMServer = require('react-dom/server.browser');
3941
ReactDOMClient = require('react-dom/client');
4042
useDeferredValue = React.useDeferredValue;
@@ -76,7 +78,11 @@ describe('ReactDOMFizzForm', () => {
7678
return useDeferredValue('Final', 'Initial');
7779
}
7880

79-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
81+
let streamPromise;
82+
await act(async () => {
83+
streamPromise = ReactDOMServer.renderToReadableStream(<App />);
84+
});
85+
const stream = await streamPromise;
8086
await readIntoContainer(stream);
8187
expect(container.textContent).toEqual('Initial');
8288

@@ -107,7 +113,11 @@ describe('ReactDOMFizzForm', () => {
107113
);
108114
}
109115

110-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
116+
let streamPromise;
117+
await act(async () => {
118+
streamPromise = ReactDOMServer.renderToReadableStream(<App />);
119+
});
120+
const stream = await streamPromise;
111121
await readIntoContainer(stream);
112122
expect(container.textContent).toEqual('Loading...');
113123

@@ -153,8 +163,12 @@ describe('ReactDOMFizzForm', () => {
153163

154164
const cRef = React.createRef();
155165

156-
// The server renders using the "initial" value for B.
157-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
166+
let streamPromise;
167+
await act(async () => {
168+
// The server renders using the "initial" value for B.
169+
streamPromise = ReactDOMServer.renderToReadableStream(<App />);
170+
});
171+
const stream = await streamPromise;
158172
await readIntoContainer(stream);
159173
assertLog(['A', 'B [Initial]', 'C']);
160174
expect(getVisibleChildren(container)).toEqual(

packages/react-dom/src/__tests__/ReactDOMFizzForm-test.js

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
'use strict';
1111

1212
import {insertNodesAndExecuteScripts} from '../test-utils/FizzTestUtils';
13+
import {patchMessageChannel} from '../../../../scripts/jest/patchMessageChannel';
1314

1415
// Polyfills for test environment
1516
global.ReadableStream =
@@ -24,10 +25,13 @@ let ReactDOMClient;
2425
let useFormStatus;
2526
let useOptimistic;
2627
let useActionState;
28+
let Scheduler;
2729

2830
describe('ReactDOMFizzForm', () => {
2931
beforeEach(() => {
3032
jest.resetModules();
33+
Scheduler = require('scheduler');
34+
patchMessageChannel(Scheduler);
3135
React = require('react');
3236
ReactDOMServer = require('react-dom/server.browser');
3337
ReactDOMClient = require('react-dom/client');
@@ -48,6 +52,14 @@ describe('ReactDOMFizzForm', () => {
4852
document.body.removeChild(container);
4953
});
5054

55+
async function serverAct(callback) {
56+
let maybePromise;
57+
await act(() => {
58+
maybePromise = callback();
59+
});
60+
return maybePromise;
61+
}
62+
5163
function submit(submitter) {
5264
const form = submitter.form || submitter;
5365
if (!submitter.form) {
@@ -96,7 +108,9 @@ describe('ReactDOMFizzForm', () => {
96108
);
97109
}
98110

99-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
111+
const stream = await serverAct(() =>
112+
ReactDOMServer.renderToReadableStream(<App />),
113+
);
100114
await readIntoContainer(stream);
101115
await act(async () => {
102116
ReactDOMClient.hydrateRoot(container, <App />);
@@ -143,7 +157,9 @@ describe('ReactDOMFizzForm', () => {
143157
);
144158
}
145159

146-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
160+
const stream = await serverAct(() =>
161+
ReactDOMServer.renderToReadableStream(<App />),
162+
);
147163
await readIntoContainer(stream);
148164
await act(async () => {
149165
ReactDOMClient.hydrateRoot(container, <App />);
@@ -175,7 +191,9 @@ describe('ReactDOMFizzForm', () => {
175191
);
176192
}
177193

178-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
194+
const stream = await serverAct(() =>
195+
ReactDOMServer.renderToReadableStream(<App />),
196+
);
179197
await readIntoContainer(stream);
180198
await expect(async () => {
181199
await act(async () => {
@@ -197,7 +215,9 @@ describe('ReactDOMFizzForm', () => {
197215
);
198216
}
199217

200-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
218+
const stream = await serverAct(() =>
219+
ReactDOMServer.renderToReadableStream(<App />),
220+
);
201221
await readIntoContainer(stream);
202222
// This should ideally warn because only the client provides a function that doesn't line up.
203223
await act(async () => {
@@ -231,7 +251,9 @@ describe('ReactDOMFizzForm', () => {
231251
);
232252
}
233253

234-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
254+
const stream = await serverAct(() =>
255+
ReactDOMServer.renderToReadableStream(<App />),
256+
);
235257
await readIntoContainer(stream);
236258
let root;
237259
await act(async () => {
@@ -278,7 +300,9 @@ describe('ReactDOMFizzForm', () => {
278300
);
279301
}
280302

281-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
303+
const stream = await serverAct(() =>
304+
ReactDOMServer.renderToReadableStream(<App />),
305+
);
282306
await readIntoContainer(stream);
283307
let root;
284308
await act(async () => {
@@ -333,13 +357,15 @@ describe('ReactDOMFizzForm', () => {
333357

334358
// Specifying the extra form fields are a DEV error, but we expect it
335359
// to eventually still be patched up after an update.
336-
await expect(async () => {
337-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
338-
await readIntoContainer(stream);
339-
}).toErrorDev([
340-
'Cannot specify a encType or method for a form that specifies a function as the action.',
341-
'Cannot specify a formTarget for a button that specifies a function as a formAction.',
342-
]);
360+
await serverAct(async () => {
361+
await expect(async () => {
362+
const stream = await ReactDOMServer.renderToReadableStream(<App />);
363+
await readIntoContainer(stream);
364+
}).toErrorDev([
365+
'Cannot specify a encType or method for a form that specifies a function as the action.',
366+
'Cannot specify a formTarget for a button that specifies a function as a formAction.',
367+
]);
368+
});
343369
let root;
344370
await expect(async () => {
345371
await act(async () => {
@@ -379,7 +405,9 @@ describe('ReactDOMFizzForm', () => {
379405
return 'Pending: ' + pending;
380406
}
381407

382-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
408+
const stream = await serverAct(() =>
409+
ReactDOMServer.renderToReadableStream(<App />),
410+
);
383411
await readIntoContainer(stream);
384412
expect(container.textContent).toBe('Pending: false');
385413

@@ -400,7 +428,9 @@ describe('ReactDOMFizzForm', () => {
400428
);
401429
}
402430

403-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
431+
const stream = await serverAct(() =>
432+
ReactDOMServer.renderToReadableStream(<App />),
433+
);
404434
await readIntoContainer(stream);
405435

406436
// Dispatch an event before hydration
@@ -441,7 +471,9 @@ describe('ReactDOMFizzForm', () => {
441471
);
442472
}
443473

444-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
474+
const stream = await serverAct(() =>
475+
ReactDOMServer.renderToReadableStream(<App />),
476+
);
445477
await readIntoContainer(stream);
446478

447479
submit(container.getElementsByTagName('input')[1]);
@@ -463,7 +495,9 @@ describe('ReactDOMFizzForm', () => {
463495
return optimisticState;
464496
}
465497

466-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
498+
const stream = await serverAct(() =>
499+
ReactDOMServer.renderToReadableStream(<App />),
500+
);
467501
await readIntoContainer(stream);
468502
expect(container.textContent).toBe('hi');
469503

@@ -484,7 +518,9 @@ describe('ReactDOMFizzForm', () => {
484518
return state;
485519
}
486520

487-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
521+
const stream = await serverAct(() =>
522+
ReactDOMServer.renderToReadableStream(<App />),
523+
);
488524
await readIntoContainer(stream);
489525
expect(container.textContent).toBe('0');
490526

@@ -521,7 +557,9 @@ describe('ReactDOMFizzForm', () => {
521557
);
522558
}
523559

524-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
560+
const stream = await serverAct(() =>
561+
ReactDOMServer.renderToReadableStream(<App />),
562+
);
525563
await readIntoContainer(stream);
526564

527565
const form = container.firstChild;
@@ -581,7 +619,9 @@ describe('ReactDOMFizzForm', () => {
581619
);
582620
}
583621

584-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
622+
const stream = await serverAct(() =>
623+
ReactDOMServer.renderToReadableStream(<App />),
624+
);
585625
await readIntoContainer(stream);
586626

587627
const input = container.getElementsByTagName('input')[1];
@@ -651,7 +691,9 @@ describe('ReactDOMFizzForm', () => {
651691
);
652692
}
653693

654-
const stream = await ReactDOMServer.renderToReadableStream(<App />);
694+
const stream = await serverAct(() =>
695+
ReactDOMServer.renderToReadableStream(<App />),
696+
);
655697
await readIntoContainer(stream);
656698

657699
const barField = container.querySelector('[name=bar]');

0 commit comments

Comments
 (0)