Skip to content

Commit 67fee58

Browse files
authored
[Fizz] Start initial work immediately (#31079)
In a recent update we make Flight start working immediately rather than waitin for a new task. This commit updates fizz to have similar mechanics. We start the render in the currently running task but we do so in a microtask to avoid reentrancy. This aligns Fizz with Flight. ref: #30961
1 parent 76aee6f commit 67fee58

File tree

3 files changed

+160
-153
lines changed

3 files changed

+160
-153
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -8119,7 +8119,7 @@ describe('ReactDOMFizzServer', () => {
81198119

81208120
prerendering = false;
81218121

8122-
const resumed = await ReactDOMFizzServer.resumeToPipeableStream(
8122+
const resumed = ReactDOMFizzServer.resumeToPipeableStream(
81238123
<App />,
81248124
JSON.parse(JSON.stringify(prerendered.postponed)),
81258125
{
@@ -8187,7 +8187,7 @@ describe('ReactDOMFizzServer', () => {
81878187
function onPostpone(reason) {
81888188
postpones.push(reason);
81898189
}
8190-
const result = await renderToPipeableStream(<App />, {
8190+
const result = renderToPipeableStream(<App />, {
81918191
onError,
81928192
onShellError,
81938193
onPostpone,

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

+124-113
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ let Stream;
1414
let React;
1515
let ReactDOMFizzServer;
1616
let Suspense;
17+
let act;
1718

1819
describe('ReactDOMFizzServerNode', () => {
1920
beforeEach(() => {
@@ -22,6 +23,7 @@ describe('ReactDOMFizzServerNode', () => {
2223
ReactDOMFizzServer = require('react-dom/server');
2324
Stream = require('stream');
2425
Suspense = React.Suspense;
26+
act = require('internal-test-utils').act;
2527
});
2628

2729
function getTestWritable() {
@@ -54,54 +56,59 @@ describe('ReactDOMFizzServerNode', () => {
5456
throw theInfinitePromise;
5557
}
5658

57-
it('should call renderToPipeableStream', () => {
59+
it('should call renderToPipeableStream', async () => {
5860
const {writable, output} = getTestWritable();
59-
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
60-
<div>hello world</div>,
61-
);
62-
pipe(writable);
63-
jest.runAllTimers();
61+
await act(() => {
62+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
63+
<div>hello world</div>,
64+
);
65+
pipe(writable);
66+
});
6467
expect(output.result).toMatchInlineSnapshot(`"<div>hello world</div>"`);
6568
});
6669

67-
it('should emit DOCTYPE at the root of the document', () => {
70+
it('should emit DOCTYPE at the root of the document', async () => {
6871
const {writable, output} = getTestWritable();
69-
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
70-
<html>
71-
<body>hello world</body>
72-
</html>,
73-
);
74-
pipe(writable);
75-
jest.runAllTimers();
72+
await act(() => {
73+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
74+
<html>
75+
<body>hello world</body>
76+
</html>,
77+
);
78+
pipe(writable);
79+
});
7680
// with Float, we emit empty heads if they are elided when rendering <html>
7781
expect(output.result).toMatchInlineSnapshot(
7882
`"<!DOCTYPE html><html><head></head><body>hello world</body></html>"`,
7983
);
8084
});
8185

82-
it('should emit bootstrap script src at the end', () => {
86+
it('should emit bootstrap script src at the end', async () => {
8387
const {writable, output} = getTestWritable();
84-
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
85-
<div>hello world</div>,
86-
{
87-
bootstrapScriptContent: 'INIT();',
88-
bootstrapScripts: ['init.js'],
89-
bootstrapModules: ['init.mjs'],
90-
},
91-
);
92-
pipe(writable);
93-
jest.runAllTimers();
88+
await act(() => {
89+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
90+
<div>hello world</div>,
91+
{
92+
bootstrapScriptContent: 'INIT();',
93+
bootstrapScripts: ['init.js'],
94+
bootstrapModules: ['init.mjs'],
95+
},
96+
);
97+
pipe(writable);
98+
});
9499
expect(output.result).toMatchInlineSnapshot(
95100
`"<link rel="preload" as="script" fetchPriority="low" href="init.js"/><link rel="modulepreload" fetchPriority="low" href="init.mjs"/><div>hello world</div><script>INIT();</script><script src="init.js" async=""></script><script type="module" src="init.mjs" async=""></script>"`,
96101
);
97102
});
98103

99-
it('should start writing after pipe', () => {
104+
it('should start writing after pipe', async () => {
100105
const {writable, output} = getTestWritable();
101-
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
102-
<div>hello world</div>,
103-
);
104-
jest.runAllTimers();
106+
let pipe;
107+
await act(() => {
108+
pipe = ReactDOMFizzServer.renderToPipeableStream(
109+
<div>hello world</div>,
110+
).pipe;
111+
});
105112
// First we write our header.
106113
output.result +=
107114
'<!doctype html><html><head><title>test</title><head><body>';
@@ -281,24 +288,26 @@ describe('ReactDOMFizzServerNode', () => {
281288
let isCompleteCalls = 0;
282289
const errors = [];
283290
const {writable, output, completed} = getTestWritable();
284-
const {pipe, abort} = ReactDOMFizzServer.renderToPipeableStream(
285-
<div>
286-
<Suspense fallback={<div>Loading</div>}>
287-
<InfiniteSuspend />
288-
</Suspense>
289-
</div>,
290-
{
291-
onError(x) {
292-
errors.push(x.message);
293-
},
294-
onAllReady() {
295-
isCompleteCalls++;
291+
let abort;
292+
await act(() => {
293+
const pipeable = ReactDOMFizzServer.renderToPipeableStream(
294+
<div>
295+
<Suspense fallback={<div>Loading</div>}>
296+
<InfiniteSuspend />
297+
</Suspense>
298+
</div>,
299+
{
300+
onError(x) {
301+
errors.push(x.message);
302+
},
303+
onAllReady() {
304+
isCompleteCalls++;
305+
},
296306
},
297-
},
298-
);
299-
pipe(writable);
300-
301-
jest.runAllTimers();
307+
);
308+
pipeable.pipe(writable);
309+
abort = pipeable.abort;
310+
});
302311

303312
expect(output.result).toContain('Loading');
304313
expect(isCompleteCalls).toBe(0);
@@ -360,26 +369,28 @@ describe('ReactDOMFizzServerNode', () => {
360369
let isCompleteCalls = 0;
361370
const errors = [];
362371
const {writable, output, completed} = getTestWritable();
363-
const {pipe, abort} = ReactDOMFizzServer.renderToPipeableStream(
364-
<div>
365-
<Suspense fallback="Loading">
366-
<Suspense fallback={<InfiniteSuspend />}>
367-
<InfiniteSuspend />
372+
let abort;
373+
await act(() => {
374+
const pipeable = ReactDOMFizzServer.renderToPipeableStream(
375+
<div>
376+
<Suspense fallback="Loading">
377+
<Suspense fallback={<InfiniteSuspend />}>
378+
<InfiniteSuspend />
379+
</Suspense>
368380
</Suspense>
369-
</Suspense>
370-
</div>,
371-
{
372-
onError(x) {
373-
errors.push(x.message);
374-
},
375-
onAllReady() {
376-
isCompleteCalls++;
381+
</div>,
382+
{
383+
onError(x) {
384+
errors.push(x.message);
385+
},
386+
onAllReady() {
387+
isCompleteCalls++;
388+
},
377389
},
378-
},
379-
);
380-
pipe(writable);
381-
382-
jest.runAllTimers();
390+
);
391+
pipeable.pipe(writable);
392+
abort = pipeable.abort;
393+
});
383394

384395
expect(output.result).toContain('Loading');
385396
expect(isCompleteCalls).toBe(0);
@@ -428,15 +439,15 @@ describe('ReactDOMFizzServerNode', () => {
428439

429440
const client = new DelayClient();
430441
const {writable, output, completed} = getTestWritable();
431-
ReactDOMFizzServer.renderToPipeableStream(
432-
<DelayContext.Provider value={client}>
433-
<Suspense fallback="loading">
434-
<Component />
435-
</Suspense>
436-
</DelayContext.Provider>,
437-
).pipe(writable);
438-
439-
jest.runAllTimers();
442+
await act(() => {
443+
ReactDOMFizzServer.renderToPipeableStream(
444+
<DelayContext.Provider value={client}>
445+
<Suspense fallback="loading">
446+
<Component />
447+
</Suspense>
448+
</DelayContext.Provider>,
449+
).pipe(writable);
450+
});
440451

441452
expect(output.error).toBe(undefined);
442453
expect(output.result).toContain('loading');
@@ -481,29 +492,28 @@ describe('ReactDOMFizzServerNode', () => {
481492
output: output0,
482493
completed: completed0,
483494
} = getTestWritable();
484-
ReactDOMFizzServer.renderToPipeableStream(
485-
<DelayContext.Provider value={client0}>
486-
<Suspense fallback="loading">
487-
<Component />
488-
</Suspense>
489-
</DelayContext.Provider>,
490-
).pipe(writable0);
491-
492495
const client1 = new DelayClient();
493496
const {
494497
writable: writable1,
495498
output: output1,
496499
completed: completed1,
497500
} = getTestWritable();
498-
ReactDOMFizzServer.renderToPipeableStream(
499-
<DelayContext.Provider value={client1}>
500-
<Suspense fallback="loading">
501-
<Component />
502-
</Suspense>
503-
</DelayContext.Provider>,
504-
).pipe(writable1);
505-
506-
jest.runAllTimers();
501+
await act(() => {
502+
ReactDOMFizzServer.renderToPipeableStream(
503+
<DelayContext.Provider value={client0}>
504+
<Suspense fallback="loading">
505+
<Component />
506+
</Suspense>
507+
</DelayContext.Provider>,
508+
).pipe(writable0);
509+
ReactDOMFizzServer.renderToPipeableStream(
510+
<DelayContext.Provider value={client1}>
511+
<Suspense fallback="loading">
512+
<Component />
513+
</Suspense>
514+
</DelayContext.Provider>,
515+
).pipe(writable1);
516+
});
507517

508518
expect(output0.error).toBe(undefined);
509519
expect(output0.result).toContain('loading');
@@ -552,22 +562,22 @@ describe('ReactDOMFizzServerNode', () => {
552562

553563
const client = new DelayClient();
554564
const {writable, output, completed} = getTestWritable();
555-
ReactDOMFizzServer.renderToPipeableStream(
556-
<>
557-
<DelayContext.Provider value={client}>
558-
<Suspense fallback="loading">
559-
<Component />
560-
</Suspense>
561-
</DelayContext.Provider>
562-
<DelayContext.Provider value={client}>
563-
<Suspense fallback="loading">
564-
<Component />
565-
</Suspense>
566-
</DelayContext.Provider>
567-
</>,
568-
).pipe(writable);
569-
570-
jest.runAllTimers();
565+
await act(() => {
566+
ReactDOMFizzServer.renderToPipeableStream(
567+
<>
568+
<DelayContext.Provider value={client}>
569+
<Suspense fallback="loading">
570+
<Component />
571+
</Suspense>
572+
</DelayContext.Provider>
573+
<DelayContext.Provider value={client}>
574+
<Suspense fallback="loading">
575+
<Component />
576+
</Suspense>
577+
</DelayContext.Provider>
578+
</>,
579+
).pipe(writable);
580+
});
571581

572582
expect(output.error).toBe(undefined);
573583
expect(output.result).toContain('loading');
@@ -630,13 +640,14 @@ describe('ReactDOMFizzServerNode', () => {
630640
expect(isComplete).toBe(true);
631641
});
632642

633-
it('should encode multibyte characters correctly without nulls (#24985)', () => {
643+
it('should encode multibyte characters correctly without nulls (#24985)', async () => {
634644
const {writable, output} = getTestWritable();
635-
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
636-
<div>{Array(700).fill('ののの')}</div>,
637-
);
638-
pipe(writable);
639-
jest.runAllTimers();
645+
await act(() => {
646+
const {pipe} = ReactDOMFizzServer.renderToPipeableStream(
647+
<div>{Array(700).fill('ののの')}</div>,
648+
);
649+
pipe(writable);
650+
});
640651
expect(output.result.indexOf('\u0000')).toBe(-1);
641652
expect(output.result).toEqual(
642653
'<div>' + Array(700).fill('ののの').join('<!-- -->') + '</div>',

0 commit comments

Comments
 (0)