@samitouri / QOS-React-2 / commits / 55dd0b1d0e

Stop using Scheduler.log to test double invocations (#29008)

Sebastian Silbermann committed May 20, 2024 at 15:30 UTC 55dd0b1d0e2a17f137579f85016e69782cad7cf7
3 files changed +322 -213
packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js
+5 -1
@@ -161,7 +161,11 @@ describe('ReactInternalTestUtils', () => {
161
162 const root = ReactNoop.createRoot();
163 await act(() => {
164 - root.render(<App />);
164 + root.render(
165 + <React.StrictMode>
166 + <App />
167 + </React.StrictMode>
168 + );
169 });
170 assertLog(['A', 'B', 'C']);
171 });
packages/react-reconciler/src/__tests__/StrictEffectsMode-test.js
+156 -136
@@ -11,33 +11,29 @@
11
12 let React;
13 let ReactNoop;
14 -let Scheduler;
14 let act;
16 -let assertLog;
15
16 describe('StrictEffectsMode', () => {
17 beforeEach(() => {
18 jest.resetModules();
19 act = require('internal-test-utils').act;
22 - const InternalTestUtils = require('internal-test-utils');
23 - assertLog = InternalTestUtils.assertLog;
20
21 React = require('react');
26 - Scheduler = require('scheduler');
22 ReactNoop = require('react-noop-renderer');
23 });
24
25 // @gate !disableLegacyMode
26 it('should not double invoke effects in legacy mode', async () => {
27 + const log = [];
28 function App({text}) {
29 React.useEffect(() => {
34 - Scheduler.log('useEffect mount');
35 - return () => Scheduler.log('useEffect unmount');
30 + log.push('useEffect mount');
31 + return () => log.push('useEffect unmount');
32 });
33
34 React.useLayoutEffect(() => {
39 - Scheduler.log('useLayoutEffect mount');
40 - return () => Scheduler.log('useLayoutEffect unmount');
35 + log.push('useLayoutEffect mount');
36 + return () => log.push('useLayoutEffect unmount');
37 });
38
39 return text;
@@ -52,19 +48,20 @@ describe('StrictEffectsMode', () => {
48 );
49 });
50
55 - assertLog(['useLayoutEffect mount', 'useEffect mount']);
51 + expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
52 });
53
54 it('double invoking for effects works properly', async () => {
55 + const log = [];
56 function App({text}) {
57 React.useEffect(() => {
61 - Scheduler.log('useEffect mount');
62 - return () => Scheduler.log('useEffect unmount');
58 + log.push('useEffect mount');
59 + return () => log.push('useEffect unmount');
60 });
61
62 React.useLayoutEffect(() => {
66 - Scheduler.log('useLayoutEffect mount');
67 - return () => Scheduler.log('useLayoutEffect unmount');
63 + log.push('useLayoutEffect mount');
64 + return () => log.push('useLayoutEffect unmount');
65 });
66
67 return text;
@@ -80,7 +77,7 @@ describe('StrictEffectsMode', () => {
77 });
78
79 if (__DEV__) {
83 - assertLog([
80 + expect(log).toEqual([
81 'useLayoutEffect mount',
82 'useEffect mount',
83 'useLayoutEffect unmount',
@@ -89,9 +86,10 @@ describe('StrictEffectsMode', () => {
86 'useEffect mount',
87 ]);
88 } else {
92 - assertLog(['useLayoutEffect mount', 'useEffect mount']);
89 + expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
90 }
91
92 + log.length = 0;
93 await act(() => {
94 ReactNoop.renderToRootWithID(
95 <React.StrictMode>
@@ -101,30 +99,32 @@ describe('StrictEffectsMode', () => {
99 );
100 });
101
104 - assertLog([
102 + expect(log).toEqual([
103 'useLayoutEffect unmount',
104 'useLayoutEffect mount',
105 'useEffect unmount',
106 'useEffect mount',
107 ]);
108
109 + log.length = 0;
110 await act(() => {
111 ReactNoop.unmountRootWithID('root');
112 });
113
115 - assertLog(['useLayoutEffect unmount', 'useEffect unmount']);
114 + expect(log).toEqual(['useLayoutEffect unmount', 'useEffect unmount']);
115 });
116
117 it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', async () => {
118 + const log = [];
119 function App({text}) {
120 React.useEffect(() => {
121 - Scheduler.log('useEffect One mount');
122 - return () => Scheduler.log('useEffect One unmount');
121 + log.push('useEffect One mount');
122 + return () => log.push('useEffect One unmount');
123 });
124
125 React.useEffect(() => {
126 - Scheduler.log('useEffect Two mount');
127 - return () => Scheduler.log('useEffect Two unmount');
126 + log.push('useEffect Two mount');
127 + return () => log.push('useEffect Two unmount');
128 });
129
130 return text;
@@ -140,7 +140,7 @@ describe('StrictEffectsMode', () => {
140 });
141
142 if (__DEV__) {
143 - assertLog([
143 + expect(log).toEqual([
144 'useEffect One mount',
145 'useEffect Two mount',
146 'useEffect One unmount',
@@ -149,9 +149,10 @@ describe('StrictEffectsMode', () => {
149 'useEffect Two mount',
150 ]);
151 } else {
152 - assertLog(['useEffect One mount', 'useEffect Two mount']);
152 + expect(log).toEqual(['useEffect One mount', 'useEffect Two mount']);
153 }
154
155 + log.length = 0;
156 await act(() => {
157 ReactNoop.renderToRootWithID(
158 <React.StrictMode>
@@ -161,30 +162,32 @@ describe('StrictEffectsMode', () => {
162 );
163 });
164
164 - assertLog([
165 + expect(log).toEqual([
166 'useEffect One unmount',
167 'useEffect Two unmount',
168 'useEffect One mount',
169 'useEffect Two mount',
170 ]);
171
172 + log.length = 0;
173 await act(() => {
174 ReactNoop.unmountRootWithID('root');
175 });
176
175 - assertLog(['useEffect One unmount', 'useEffect Two unmount']);
177 + expect(log).toEqual(['useEffect One unmount', 'useEffect Two unmount']);
178 });
179
180 it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', async () => {
181 + const log = [];
182 function App({text}) {
183 React.useLayoutEffect(() => {
181 - Scheduler.log('useLayoutEffect One mount');
182 - return () => Scheduler.log('useLayoutEffect One unmount');
184 + log.push('useLayoutEffect One mount');
185 + return () => log.push('useLayoutEffect One unmount');
186 });
187
188 React.useLayoutEffect(() => {
186 - Scheduler.log('useLayoutEffect Two mount');
187 - return () => Scheduler.log('useLayoutEffect Two unmount');
189 + log.push('useLayoutEffect Two mount');
190 + return () => log.push('useLayoutEffect Two unmount');
191 });
192
193 return text;
@@ -200,7 +203,7 @@ describe('StrictEffectsMode', () => {
203 });
204
205 if (__DEV__) {
203 - assertLog([
206 + expect(log).toEqual([
207 'useLayoutEffect One mount',
208 'useLayoutEffect Two mount',
209 'useLayoutEffect One unmount',
@@ -209,9 +212,13 @@ describe('StrictEffectsMode', () => {
212 'useLayoutEffect Two mount',
213 ]);
214 } else {
212 - assertLog(['useLayoutEffect One mount', 'useLayoutEffect Two mount']);
215 + expect(log).toEqual([
216 + 'useLayoutEffect One mount',
217 + 'useLayoutEffect Two mount',
218 + ]);
219 }
220
221 + log.length = 0;
222 await act(() => {
223 ReactNoop.renderToRootWithID(
224 <React.StrictMode>
@@ -221,28 +228,33 @@ describe('StrictEffectsMode', () => {
228 );
229 });
230
224 - assertLog([
231 + expect(log).toEqual([
232 'useLayoutEffect One unmount',
233 'useLayoutEffect Two unmount',
234 'useLayoutEffect One mount',
235 'useLayoutEffect Two mount',
236 ]);
237
238 + log.length = 0;
239 await act(() => {
240 ReactNoop.unmountRootWithID('root');
241 });
242
235 - assertLog(['useLayoutEffect One unmount', 'useLayoutEffect Two unmount']);
243 + expect(log).toEqual([
244 + 'useLayoutEffect One unmount',
245 + 'useLayoutEffect Two unmount',
246 + ]);
247 });
248
249 it('useEffect and useLayoutEffect is called twice when there is no unmount', async () => {
250 + const log = [];
251 function App({text}) {
252 React.useEffect(() => {
241 - Scheduler.log('useEffect mount');
253 + log.push('useEffect mount');
254 });
255
256 React.useLayoutEffect(() => {
245 - Scheduler.log('useLayoutEffect mount');
257 + log.push('useLayoutEffect mount');
258 });
259
260 return text;
@@ -257,16 +269,17 @@ describe('StrictEffectsMode', () => {
269 });
270
271 if (__DEV__) {
260 - assertLog([
272 + expect(log).toEqual([
273 'useLayoutEffect mount',
274 'useEffect mount',
275 'useLayoutEffect mount',
276 'useEffect mount',
277 ]);
278 } else {
267 - assertLog(['useLayoutEffect mount', 'useEffect mount']);
279 + expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
280 }
281
282 + log.length = 0;
283 await act(() => {
284 ReactNoop.renderToRootWithID(
285 <React.StrictMode>
@@ -275,32 +288,34 @@ describe('StrictEffectsMode', () => {
288 );
289 });
290
278 - assertLog(['useLayoutEffect mount', 'useEffect mount']);
291 + expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
292
293 + log.length = 0;
294 await act(() => {
295 ReactNoop.unmountRootWithID('root');
296 });
297
284 - assertLog([]);
298 + expect(log).toEqual([]);
299 });
300
301 it('passes the right context to class component lifecycles', async () => {
302 + const log = [];
303 class App extends React.PureComponent {
304 test() {}
305
306 componentDidMount() {
307 this.test();
293 - Scheduler.log('componentDidMount');
308 + log.push('componentDidMount');
309 }
310
311 componentDidUpdate() {
312 this.test();
298 - Scheduler.log('componentDidUpdate');
313 + log.push('componentDidUpdate');
314 }
315
316 componentWillUnmount() {
317 this.test();
303 - Scheduler.log('componentWillUnmount');
318 + log.push('componentWillUnmount');
319 }
320
321 render() {
@@ -317,28 +332,29 @@ describe('StrictEffectsMode', () => {
332 });
333
334 if (__DEV__) {
320 - assertLog([
335 + expect(log).toEqual([
336 'componentDidMount',
337 'componentWillUnmount',
338 'componentDidMount',
339 ]);
340 } else {
326 - assertLog(['componentDidMount']);
341 + expect(log).toEqual(['componentDidMount']);
342 }
343 });
344
345 it('double invoking works for class components', async () => {
346 + const log = [];
347 class App extends React.PureComponent {
348 componentDidMount() {
333 - Scheduler.log('componentDidMount');
349 + log.push('componentDidMount');
350 }
351
352 componentDidUpdate() {
337 - Scheduler.log('componentDidUpdate');
353 + log.push('componentDidUpdate');
354 }
355
356 componentWillUnmount() {
341 - Scheduler.log('componentWillUnmount');
357 + log.push('componentWillUnmount');
358 }
359
360 render() {
@@ -356,15 +372,16 @@ describe('StrictEffectsMode', () => {
372 });
373
374 if (__DEV__) {
359 - assertLog([
375 + expect(log).toEqual([
376 'componentDidMount',
377 'componentWillUnmount',
378 'componentDidMount',
379 ]);
380 } else {
365 - assertLog(['componentDidMount']);
381 + expect(log).toEqual(['componentDidMount']);
382 }
383
384 + log.length = 0;
385 await act(() => {
386 ReactNoop.renderToRootWithID(
387 <React.StrictMode>
@@ -374,23 +391,25 @@ describe('StrictEffectsMode', () => {
391 );
392 });
393
377 - assertLog(['componentDidUpdate']);
394 + expect(log).toEqual(['componentDidUpdate']);
395
396 + log.length = 0;
397 await act(() => {
398 ReactNoop.unmountRootWithID('root');
399 });
400
383 - assertLog(['componentWillUnmount']);
401 + expect(log).toEqual(['componentWillUnmount']);
402 });
403
404 it('invokes componentWillUnmount for class components without componentDidMount', async () => {
405 + const log = [];
406 class App extends React.PureComponent {
407 componentDidUpdate() {
389 - Scheduler.log('componentDidUpdate');
408 + log.push('componentDidUpdate');
409 }
410
411 componentWillUnmount() {
393 - Scheduler.log('componentWillUnmount');
412 + log.push('componentWillUnmount');
413 }
414
415 render() {
@@ -408,11 +427,12 @@ describe('StrictEffectsMode', () => {
427 });
428
429 if (__DEV__) {
411 - assertLog(['componentWillUnmount']);
430 + expect(log).toEqual(['componentWillUnmount']);
431 } else {
413 - assertLog([]);
432 + expect(log).toEqual([]);
433 }
434
435 + log.length = 0;
436 await act(() => {
437 ReactNoop.renderToRootWithID(
438 <React.StrictMode>
@@ -422,28 +442,30 @@ describe('StrictEffectsMode', () => {
442 );
443 });
444
425 - assertLog(['componentDidUpdate']);
445 + expect(log).toEqual(['componentDidUpdate']);
446
447 + log.length = 0;
448 await act(() => {
449 ReactNoop.unmountRootWithID('root');
450 });
451
431 - assertLog(['componentWillUnmount']);
452 + expect(log).toEqual(['componentWillUnmount']);
453 });
454
455 // @gate !disableLegacyMode
456 it('should not double invoke class lifecycles in legacy mode', async () => {
457 + const log = [];
458 class App extends React.PureComponent {
459 componentDidMount() {
438 - Scheduler.log('componentDidMount');
460 + log.push('componentDidMount');
461 }
462
463 componentDidUpdate() {
442 - Scheduler.log('componentDidUpdate');
464 + log.push('componentDidUpdate');
465 }
466
467 componentWillUnmount() {
446 - Scheduler.log('componentWillUnmount');
468 + log.push('componentWillUnmount');
469 }
470
471 render() {
@@ -460,26 +482,27 @@ describe('StrictEffectsMode', () => {
482 );
483 });
484
463 - assertLog(['componentDidMount']);
485 + expect(log).toEqual(['componentDidMount']);
486 });
487
488 it('double flushing passive effects only results in one double invoke', async () => {
489 + const log = [];
490 function App({text}) {
491 const [state, setState] = React.useState(0);
492 React.useEffect(() => {
493 if (state !== 1) {
494 setState(1);
495 }
473 - Scheduler.log('useEffect mount');
474 - return () => Scheduler.log('useEffect unmount');
496 + log.push('useEffect mount');
497 + return () => log.push('useEffect unmount');
498 });
499
500 React.useLayoutEffect(() => {
478 - Scheduler.log('useLayoutEffect mount');
479 - return () => Scheduler.log('useLayoutEffect unmount');
501 + log.push('useLayoutEffect mount');
502 + return () => log.push('useLayoutEffect unmount');
503 });
504
482 - Scheduler.log(text);
505 + log.push(text);
506 return text;
507 }
508
@@ -492,7 +515,8 @@ describe('StrictEffectsMode', () => {
515 });
516
517 if (__DEV__) {
495 - assertLog([
518 + expect(log).toEqual([
519 + 'mount',
520 'mount',
521 'useLayoutEffect mount',
522 'useEffect mount',
@@ -501,13 +525,14 @@ describe('StrictEffectsMode', () => {
525 'useLayoutEffect mount',
526 'useEffect mount',
527 'mount',
528 + 'mount',
529 'useLayoutEffect unmount',
530 'useLayoutEffect mount',
531 'useEffect unmount',
532 'useEffect mount',
533 ]);
534 } else {
510 - assertLog([
535 + expect(log).toEqual([
536 'mount',
537 'useLayoutEffect mount',
538 'useEffect mount',
@@ -521,15 +546,16 @@ describe('StrictEffectsMode', () => {
546 });
547
548 it('newly mounted components after initial mount get double invoked', async () => {
549 + const log = [];
550 let _setShowChild;
551 function Child() {
552 React.useEffect(() => {
527 - Scheduler.log('Child useEffect mount');
528 - return () => Scheduler.log('Child useEffect unmount');
553 + log.push('Child useEffect mount');
554 + return () => log.push('Child useEffect unmount');
555 });
556 React.useLayoutEffect(() => {
531 - Scheduler.log('Child useLayoutEffect mount');
532 - return () => Scheduler.log('Child useLayoutEffect unmount');
557 + log.push('Child useLayoutEffect mount');
558 + return () => log.push('Child useLayoutEffect unmount');
559 });
560
561 return null;
@@ -539,12 +565,12 @@ describe('StrictEffectsMode', () => {
565 const [showChild, setShowChild] = React.useState(false);
566 _setShowChild = setShowChild;
567 React.useEffect(() => {
542 - Scheduler.log('App useEffect mount');
543 - return () => Scheduler.log('App useEffect unmount');
568 + log.push('App useEffect mount');
569 + return () => log.push('App useEffect unmount');
570 });
571 React.useLayoutEffect(() => {
546 - Scheduler.log('App useLayoutEffect mount');
547 - return () => Scheduler.log('App useLayoutEffect unmount');
572 + log.push('App useLayoutEffect mount');
573 + return () => log.push('App useLayoutEffect unmount');
574 });
575
576 return showChild && <Child />;
@@ -560,7 +586,7 @@ describe('StrictEffectsMode', () => {
586 });
587
588 if (__DEV__) {
563 - assertLog([
589 + expect(log).toEqual([
590 'App useLayoutEffect mount',
591 'App useEffect mount',
592 'App useLayoutEffect unmount',
@@ -569,15 +595,16 @@ describe('StrictEffectsMode', () => {
595 'App useEffect mount',
596 ]);
597 } else {
572 - assertLog(['App useLayoutEffect mount', 'App useEffect mount']);
598 + expect(log).toEqual(['App useLayoutEffect mount', 'App useEffect mount']);
599 }
600
601 + log.length = 0;
602 await act(() => {
603 _setShowChild(true);
604 });
605
606 if (__DEV__) {
580 - assertLog([
607 + expect(log).toEqual([
608 'App useLayoutEffect unmount',
609 'Child useLayoutEffect mount',
610 'App useLayoutEffect mount',
@@ -590,7 +617,7 @@ describe('StrictEffectsMode', () => {
617 'Child useEffect mount',
618 ]);
619 } else {
593 - assertLog([
620 + expect(log).toEqual([
621 'App useLayoutEffect unmount',
622 'Child useLayoutEffect mount',
623 'App useLayoutEffect mount',
@@ -602,13 +629,14 @@ describe('StrictEffectsMode', () => {
629 });
630
631 it('classes and functions are double invoked together correctly', async () => {
632 + const log = [];
633 class ClassChild extends React.PureComponent {
634 componentDidMount() {
607 - Scheduler.log('componentDidMount');
635 + log.push('componentDidMount');
636 }
637
638 componentWillUnmount() {
611 - Scheduler.log('componentWillUnmount');
639 + log.push('componentWillUnmount');
640 }
641
642 render() {
@@ -618,12 +646,12 @@ describe('StrictEffectsMode', () => {
646
647 function FunctionChild({text}) {
648 React.useEffect(() => {
621 - Scheduler.log('useEffect mount');
622 - return () => Scheduler.log('useEffect unmount');
649 + log.push('useEffect mount');
650 + return () => log.push('useEffect unmount');
651 });
652 React.useLayoutEffect(() => {
625 - Scheduler.log('useLayoutEffect mount');
626 - return () => Scheduler.log('useLayoutEffect unmount');
653 + log.push('useLayoutEffect mount');
654 + return () => log.push('useLayoutEffect unmount');
655 });
656 return text;
657 }
@@ -647,7 +675,7 @@ describe('StrictEffectsMode', () => {
675 });
676
677 if (__DEV__) {
650 - assertLog([
678 + expect(log).toEqual([
679 'componentDidMount',
680 'useLayoutEffect mount',
681 'useEffect mount',
@@ -659,13 +687,14 @@ describe('StrictEffectsMode', () => {
687 'useEffect mount',
688 ]);
689 } else {
662 - assertLog([
690 + expect(log).toEqual([
691 'componentDidMount',
692 'useLayoutEffect mount',
693 'useEffect mount',
694 ]);
695 }
696
697 + log.length = 0;
698 await act(() => {
699 ReactNoop.renderToRootWithID(
700 <React.StrictMode>
@@ -675,18 +704,19 @@ describe('StrictEffectsMode', () => {
704 );
705 });
706
678 - assertLog([
707 + expect(log).toEqual([
708 'useLayoutEffect unmount',
709 'useLayoutEffect mount',
710 'useEffect unmount',
711 'useEffect mount',
712 ]);
713
714 + log.length = 0;
715 await act(() => {
716 ReactNoop.unmountRootWithID('root');
717 });
718
689 - assertLog([
719 + expect(log).toEqual([
720 'componentWillUnmount',
721 'useLayoutEffect unmount',
722 'useEffect unmount',
@@ -694,9 +724,10 @@ describe('StrictEffectsMode', () => {
724 });
725
726 it('classes without componentDidMount and functions are double invoked together correctly', async () => {
727 + const log = [];
728 class ClassChild extends React.PureComponent {
729 componentWillUnmount() {
699 - Scheduler.log('componentWillUnmount');
730 + log.push('componentWillUnmount');
731 }
732
733 render() {
@@ -706,12 +737,12 @@ describe('StrictEffectsMode', () => {
737
738 function FunctionChild({text}) {
739 React.useEffect(() => {
709 - Scheduler.log('useEffect mount');
710 - return () => Scheduler.log('useEffect unmount');
740 + log.push('useEffect mount');
741 + return () => log.push('useEffect unmount');
742 });
743 React.useLayoutEffect(() => {
713 - Scheduler.log('useLayoutEffect mount');
714 - return () => Scheduler.log('useLayoutEffect unmount');
744 + log.push('useLayoutEffect mount');
745 + return () => log.push('useLayoutEffect unmount');
746 });
747 return text;
748 }
@@ -735,7 +766,7 @@ describe('StrictEffectsMode', () => {
766 });
767
768 if (__DEV__) {
738 - assertLog([
769 + expect(log).toEqual([
770 'useLayoutEffect mount',
771 'useEffect mount',
772 'componentWillUnmount',
@@ -745,9 +776,10 @@ describe('StrictEffectsMode', () => {
776 'useEffect mount',
777 ]);
778 } else {
748 - assertLog(['useLayoutEffect mount', 'useEffect mount']);
779 + expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
780 }
781
782 + log.length = 0;
783 await act(() => {
784 ReactNoop.renderToRootWithID(
785 <React.StrictMode>
@@ -757,18 +789,19 @@ describe('StrictEffectsMode', () => {
789 );
790 });
791
760 - assertLog([
792 + expect(log).toEqual([
793 'useLayoutEffect unmount',
794 'useLayoutEffect mount',
795 'useEffect unmount',
796 'useEffect mount',
797 ]);
798
799 + log.length = 0;
800 await act(() => {
801 ReactNoop.unmountRootWithID('root');
802 });
803
771 - assertLog([
804 + expect(log).toEqual([
805 'componentWillUnmount',
806 'useLayoutEffect unmount',
807 'useEffect unmount',
@@ -777,7 +810,7 @@ describe('StrictEffectsMode', () => {
810
811 // @gate __DEV__
812 it('should double invoke effects after a re-suspend', async () => {
780 - // Not using Scheduler.log because it silences double render logs.
813 + // Not using log.push because it silences double render logs.
814 let log = [];
815 let shouldSuspend = true;
816 let resolve;
@@ -867,8 +900,17 @@ describe('StrictEffectsMode', () => {
900 );
901 });
902
903 + expect(log).toEqual([
904 + 'Parent rendered',
905 + 'Parent rendered',
906 + 'Child rendered',
907 + 'Child suspended',
908 + 'Fallback',
909 + 'Fallback',
910 + ]);
911 +
912 + log = [];
913 // while suspended, update
871 - log.push('-----------------------after update');
914 await act(() => {
915 ReactNoop.render(
916 <React.StrictMode>
@@ -877,9 +919,19 @@ describe('StrictEffectsMode', () => {
919 );
920 });
921
880 - // Now resolve and commit
881 - log.push('-----------------------after suspense');
922 + expect(log).toEqual([
923 + 'Parent rendered',
924 + 'Parent rendered',
925 + 'Child rendered',
926 + 'Child suspended',
927 + 'Fallback',
928 + 'Fallback',
929 + 'Parent dep destroy',
930 + 'Parent dep create',
931 + ]);
932
933 + log = [];
934 + // Now resolve and commit
935 await act(() => {
936 resolve();
937 shouldSuspend = false;
@@ -887,22 +939,6 @@ describe('StrictEffectsMode', () => {
939
940 if (gate(flags => flags.useModernStrictMode)) {
941 expect(log).toEqual([
890 - 'Parent rendered',
891 - 'Parent rendered',
892 - 'Child rendered',
893 - 'Child suspended',
894 - 'Fallback',
895 - 'Fallback',
896 - '-----------------------after update',
897 - 'Parent rendered',
898 - 'Parent rendered',
899 - 'Child rendered',
900 - 'Child suspended',
901 - 'Fallback',
902 - 'Fallback',
903 - 'Parent dep destroy',
904 - 'Parent dep create',
905 - '-----------------------after suspense',
942 'Child rendered',
943 'Child rendered',
944 // !!! Committed, destroy and create effect.
@@ -924,22 +960,6 @@ describe('StrictEffectsMode', () => {
960 ]);
961 } else {
962 expect(log).toEqual([
927 - 'Parent rendered',
928 - 'Parent rendered',
929 - 'Child rendered',
930 - 'Child suspended',
931 - 'Fallback',
932 - 'Fallback',
933 - '-----------------------after update',
934 - 'Parent rendered',
935 - 'Parent rendered',
936 - 'Child rendered',
937 - 'Child suspended',
938 - 'Fallback',
939 - 'Fallback',
940 - 'Parent dep destroy',
941 - 'Parent dep create',
942 - '-----------------------after suspense',
963 'Child rendered',
964 'Child rendered',
965 'Child dep destroy',
packages/react-reconciler/src/__tests__/StrictEffectsModeDefaults-test.internal.js
+161 -76
@@ -13,7 +13,6 @@ let React;
13 let ReactNoop;
14 let Scheduler;
15 let act;
16 -let assertLog;
16 let waitFor;
17 let waitForAll;
18 let waitForPaint;
@@ -31,20 +30,20 @@ describe('StrictEffectsMode defaults', () => {
30 waitFor = InternalTestUtils.waitFor;
31 waitForAll = InternalTestUtils.waitForAll;
32 waitForPaint = InternalTestUtils.waitForPaint;
34 - assertLog = InternalTestUtils.assertLog;
33 });
34
35 // @gate !disableLegacyMode
36 it('should not double invoke effects in legacy mode', async () => {
37 + const log = [];
38 function App({text}) {
39 React.useEffect(() => {
41 - Scheduler.log('useEffect mount');
42 - return () => Scheduler.log('useEffect unmount');
40 + log.push('useEffect mount');
41 + return () => log.push('useEffect unmount');
42 });
43
44 React.useLayoutEffect(() => {
46 - Scheduler.log('useLayoutEffect mount');
47 - return () => Scheduler.log('useLayoutEffect unmount');
45 + log.push('useLayoutEffect mount');
46 + return () => log.push('useLayoutEffect unmount');
47 });
48
49 return text;
@@ -58,22 +57,23 @@ describe('StrictEffectsMode defaults', () => {
57 );
58 });
59
61 - assertLog(['useLayoutEffect mount', 'useEffect mount']);
60 + expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
61 });
62
63 // @gate !disableLegacyMode
64 it('should not double invoke class lifecycles in legacy mode', async () => {
65 + const log = [];
66 class App extends React.PureComponent {
67 componentDidMount() {
68 - Scheduler.log('componentDidMount');
68 + log.push('componentDidMount');
69 }
70
71 componentDidUpdate() {
72 - Scheduler.log('componentDidUpdate');
72 + log.push('componentDidUpdate');
73 }
74
75 componentWillUnmount() {
76 - Scheduler.log('componentWillUnmount');
76 + log.push('componentWillUnmount');
77 }
78
79 render() {
@@ -89,15 +89,20 @@ describe('StrictEffectsMode defaults', () => {
89 );
90 });
91
92 - assertLog(['componentDidMount']);
92 + expect(log).toEqual(['componentDidMount']);
93 });
94
95 if (__DEV__) {
96 it('should flush double-invoked effects within the same frame as layout effects if there are no passive effects', async () => {
97 + const log = [];
98 function ComponentWithEffects({label}) {
99 React.useLayoutEffect(() => {
100 Scheduler.log(`useLayoutEffect mount "${label}"`);
100 - return () => Scheduler.log(`useLayoutEffect unmount "${label}"`);
101 + log.push(`useLayoutEffect mount "${label}"`);
102 + return () => {
103 + Scheduler.log(`useLayoutEffect unmount "${label}"`);
104 + log.push(`useLayoutEffect unmount "${label}"`);
105 + };
106 });
107
108 return label;
@@ -115,8 +120,14 @@ describe('StrictEffectsMode defaults', () => {
120 'useLayoutEffect unmount "one"',
121 'useLayoutEffect mount "one"',
122 ]);
123 + expect(log).toEqual([
124 + 'useLayoutEffect mount "one"',
125 + 'useLayoutEffect unmount "one"',
126 + 'useLayoutEffect mount "one"',
127 + ]);
128 });
129
130 + log.length = 0;
131 await act(async () => {
132 ReactNoop.render(
133 <React.StrictMode>
@@ -125,13 +136,23 @@ describe('StrictEffectsMode defaults', () => {
136 </React.StrictMode>,
137 );
138
128 - assertLog([]);
139 + expect(log).toEqual([]);
140 await waitForPaint([
141 // Cleanup and re-run "one" (and "two") since there is no dependencies array.
142 'useLayoutEffect unmount "one"',
143 'useLayoutEffect mount "one"',
144 'useLayoutEffect mount "two"',
145
146 + // Since "two" is new, it should be double-invoked.
147 + 'useLayoutEffect unmount "two"',
148 + 'useLayoutEffect mount "two"',
149 + ]);
150 + expect(log).toEqual([
151 + // Cleanup and re-run "one" (and "two") since there is no dependencies array.
152 + 'useLayoutEffect unmount "one"',
153 + 'useLayoutEffect mount "one"',
154 + 'useLayoutEffect mount "two"',
155 +
156 // Since "two" is new, it should be double-invoked.
157 'useLayoutEffect unmount "two"',
158 'useLayoutEffect mount "two"',
@@ -142,15 +163,24 @@ describe('StrictEffectsMode defaults', () => {
163 // This test also verifies that double-invoked effects flush synchronously
164 // within the same frame as passive effects.
165 it('should double invoke effects only for newly mounted components', async () => {
166 + const log = [];
167 function ComponentWithEffects({label}) {
168 React.useEffect(() => {
169 + log.push(`useEffect mount "${label}"`);
170 Scheduler.log(`useEffect mount "${label}"`);
148 - return () => Scheduler.log(`useEffect unmount "${label}"`);
171 + return () => {
172 + log.push(`useEffect unmount "${label}"`);
173 + Scheduler.log(`useEffect unmount "${label}"`);
174 + };
175 });
176
177 React.useLayoutEffect(() => {
178 + log.push(`useLayoutEffect mount "${label}"`);
179 Scheduler.log(`useLayoutEffect mount "${label}"`);
153 - return () => Scheduler.log(`useLayoutEffect unmount "${label}"`);
180 + return () => {
181 + log.push(`useLayoutEffect unmount "${label}"`);
182 + Scheduler.log(`useLayoutEffect unmount "${label}"`);
183 + };
184 });
185
186 return label;
@@ -171,8 +201,17 @@ describe('StrictEffectsMode defaults', () => {
201 'useLayoutEffect mount "one"',
202 'useEffect mount "one"',
203 ]);
204 + expect(log).toEqual([
205 + 'useLayoutEffect mount "one"',
206 + 'useEffect mount "one"',
207 + 'useLayoutEffect unmount "one"',
208 + 'useEffect unmount "one"',
209 + 'useLayoutEffect mount "one"',
210 + 'useEffect mount "one"',
211 + ]);
212 });
213
214 + log.length = 0;
215 await act(async () => {
216 ReactNoop.render(
217 <React.StrictMode>
@@ -187,11 +226,29 @@ describe('StrictEffectsMode defaults', () => {
226 'useLayoutEffect mount "one"',
227 'useLayoutEffect mount "two"',
228 ]);
229 + expect(log).toEqual([
230 + // Cleanup and re-run "one" (and "two") since there is no dependencies array.
231 + 'useLayoutEffect unmount "one"',
232 + 'useLayoutEffect mount "one"',
233 + 'useLayoutEffect mount "two"',
234 + ]);
235 + log.length = 0;
236 await waitForAll([
237 'useEffect unmount "one"',
238 'useEffect mount "one"',
239 'useEffect mount "two"',
240
241 + // Since "two" is new, it should be double-invoked.
242 + 'useLayoutEffect unmount "two"',
243 + 'useEffect unmount "two"',
244 + 'useLayoutEffect mount "two"',
245 + 'useEffect mount "two"',
246 + ]);
247 + expect(log).toEqual([
248 + 'useEffect unmount "one"',
249 + 'useEffect mount "one"',
250 + 'useEffect mount "two"',
251 +
252 // Since "two" is new, it should be double-invoked.
253 'useLayoutEffect unmount "two"',
254 'useEffect unmount "two"',
@@ -202,15 +259,16 @@ describe('StrictEffectsMode defaults', () => {
259 });
260
261 it('double invoking for effects for modern roots', async () => {
262 + const log = [];
263 function App({text}) {
264 React.useEffect(() => {
207 - Scheduler.log('useEffect mount');
208 - return () => Scheduler.log('useEffect unmount');
265 + log.push('useEffect mount');
266 + return () => log.push('useEffect unmount');
267 });
268
269 React.useLayoutEffect(() => {
212 - Scheduler.log('useLayoutEffect mount');
213 - return () => Scheduler.log('useLayoutEffect unmount');
270 + log.push('useLayoutEffect mount');
271 + return () => log.push('useLayoutEffect unmount');
272 });
273
274 return text;
@@ -223,7 +281,7 @@ describe('StrictEffectsMode defaults', () => {
281 );
282 });
283
226 - assertLog([
284 + expect(log).toEqual([
285 'useLayoutEffect mount',
286 'useEffect mount',
287 'useLayoutEffect unmount',
@@ -232,6 +290,7 @@ describe('StrictEffectsMode defaults', () => {
290 'useEffect mount',
291 ]);
292
293 + log.length = 0;
294 await act(() => {
295 ReactNoop.render(
296 <React.StrictMode>
@@ -240,30 +299,32 @@ describe('StrictEffectsMode defaults', () => {
299 );
300 });
301
243 - assertLog([
302 + expect(log).toEqual([
303 'useLayoutEffect unmount',
304 'useLayoutEffect mount',
305 'useEffect unmount',
306 'useEffect mount',
307 ]);
308
309 + log.length = 0;
310 await act(() => {
311 ReactNoop.render(null);
312 });
313
254 - assertLog(['useLayoutEffect unmount', 'useEffect unmount']);
314 + expect(log).toEqual(['useLayoutEffect unmount', 'useEffect unmount']);
315 });
316
317 it('multiple effects are double invoked in the right order (all mounted, all unmounted, all remounted)', async () => {
318 + const log = [];
319 function App({text}) {
320 React.useEffect(() => {
260 - Scheduler.log('useEffect One mount');
261 - return () => Scheduler.log('useEffect One unmount');
321 + log.push('useEffect One mount');
322 + return () => log.push('useEffect One unmount');
323 });
324
325 React.useEffect(() => {
265 - Scheduler.log('useEffect Two mount');
266 - return () => Scheduler.log('useEffect Two unmount');
326 + log.push('useEffect Two mount');
327 + return () => log.push('useEffect Two unmount');
328 });
329
330 return text;
@@ -277,7 +338,7 @@ describe('StrictEffectsMode defaults', () => {
338 );
339 });
340
280 - assertLog([
341 + expect(log).toEqual([
342 'useEffect One mount',
343 'useEffect Two mount',
344 'useEffect One unmount',
@@ -286,6 +347,7 @@ describe('StrictEffectsMode defaults', () => {
347 'useEffect Two mount',
348 ]);
349
350 + log.length = 0;
351 await act(() => {
352 ReactNoop.render(
353 <React.StrictMode>
@@ -294,30 +356,32 @@ describe('StrictEffectsMode defaults', () => {
356 );
357 });
358
297 - assertLog([
359 + expect(log).toEqual([
360 'useEffect One unmount',
361 'useEffect Two unmount',
362 'useEffect One mount',
363 'useEffect Two mount',
364 ]);
365
366 + log.length = 0;
367 await act(() => {
368 ReactNoop.render(null);
369 });
370
308 - assertLog(['useEffect One unmount', 'useEffect Two unmount']);
371 + expect(log).toEqual(['useEffect One unmount', 'useEffect Two unmount']);
372 });
373
374 it('multiple layout effects are double invoked in the right order (all mounted, all unmounted, all remounted)', async () => {
375 + const log = [];
376 function App({text}) {
377 React.useLayoutEffect(() => {
314 - Scheduler.log('useLayoutEffect One mount');
315 - return () => Scheduler.log('useLayoutEffect One unmount');
378 + log.push('useLayoutEffect One mount');
379 + return () => log.push('useLayoutEffect One unmount');
380 });
381
382 React.useLayoutEffect(() => {
319 - Scheduler.log('useLayoutEffect Two mount');
320 - return () => Scheduler.log('useLayoutEffect Two unmount');
383 + log.push('useLayoutEffect Two mount');
384 + return () => log.push('useLayoutEffect Two unmount');
385 });
386
387 return text;
@@ -331,7 +395,7 @@ describe('StrictEffectsMode defaults', () => {
395 );
396 });
397
334 - assertLog([
398 + expect(log).toEqual([
399 'useLayoutEffect One mount',
400 'useLayoutEffect Two mount',
401 'useLayoutEffect One unmount',
@@ -340,6 +404,7 @@ describe('StrictEffectsMode defaults', () => {
404 'useLayoutEffect Two mount',
405 ]);
406
407 + log.length = 0;
408 await act(() => {
409 ReactNoop.render(
410 <React.StrictMode>
@@ -348,28 +413,33 @@ describe('StrictEffectsMode defaults', () => {
413 );
414 });
415
351 - assertLog([
416 + expect(log).toEqual([
417 'useLayoutEffect One unmount',
418 'useLayoutEffect Two unmount',
419 'useLayoutEffect One mount',
420 'useLayoutEffect Two mount',
421 ]);
422
423 + log.length = 0;
424 await act(() => {
425 ReactNoop.render(null);
426 });
427
362 - assertLog(['useLayoutEffect One unmount', 'useLayoutEffect Two unmount']);
428 + expect(log).toEqual([
429 + 'useLayoutEffect One unmount',
430 + 'useLayoutEffect Two unmount',
431 + ]);
432 });
433
434 it('useEffect and useLayoutEffect is called twice when there is no unmount', async () => {
435 + const log = [];
436 function App({text}) {
437 React.useEffect(() => {
368 - Scheduler.log('useEffect mount');
438 + log.push('useEffect mount');
439 });
440
441 React.useLayoutEffect(() => {
372 - Scheduler.log('useLayoutEffect mount');
442 + log.push('useLayoutEffect mount');
443 });
444
445 return text;
@@ -383,13 +453,14 @@ describe('StrictEffectsMode defaults', () => {
453 );
454 });
455
386 - assertLog([
456 + expect(log).toEqual([
457 'useLayoutEffect mount',
458 'useEffect mount',
459 'useLayoutEffect mount',
460 'useEffect mount',
461 ]);
462
463 + log.length = 0;
464 await act(() => {
465 ReactNoop.render(
466 <React.StrictMode>
@@ -398,13 +469,14 @@ describe('StrictEffectsMode defaults', () => {
469 );
470 });
471
401 - assertLog(['useLayoutEffect mount', 'useEffect mount']);
472 + expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
473
474 + log.length = 0;
475 await act(() => {
476 ReactNoop.render(null);
477 });
478
407 - assertLog([]);
479 + expect(log).toEqual([]);
480 });
481
482 //@gate useModernStrictMode
@@ -436,22 +508,23 @@ describe('StrictEffectsMode defaults', () => {
508 });
509
510 it('passes the right context to class component lifecycles', async () => {
511 + const log = [];
512 class App extends React.PureComponent {
513 test() {}
514
515 componentDidMount() {
516 this.test();
444 - Scheduler.log('componentDidMount');
517 + log.push('componentDidMount');
518 }
519
520 componentDidUpdate() {
521 this.test();
449 - Scheduler.log('componentDidUpdate');
522 + log.push('componentDidUpdate');
523 }
524
525 componentWillUnmount() {
526 this.test();
454 - Scheduler.log('componentWillUnmount');
527 + log.push('componentWillUnmount');
528 }
529
530 render() {
@@ -467,7 +540,7 @@ describe('StrictEffectsMode defaults', () => {
540 );
541 });
542
470 - assertLog([
543 + expect(log).toEqual([
544 'componentDidMount',
545 'componentWillUnmount',
546 'componentDidMount',
@@ -475,17 +548,18 @@ describe('StrictEffectsMode defaults', () => {
548 });
549
550 it('double invoking works for class components', async () => {
551 + const log = [];
552 class App extends React.PureComponent {
553 componentDidMount() {
480 - Scheduler.log('componentDidMount');
554 + log.push('componentDidMount');
555 }
556
557 componentDidUpdate() {
484 - Scheduler.log('componentDidUpdate');
558 + log.push('componentDidUpdate');
559 }
560
561 componentWillUnmount() {
488 - Scheduler.log('componentWillUnmount');
562 + log.push('componentWillUnmount');
563 }
564
565 render() {
@@ -501,12 +575,13 @@ describe('StrictEffectsMode defaults', () => {
575 );
576 });
577
504 - assertLog([
578 + expect(log).toEqual([
579 'componentDidMount',
580 'componentWillUnmount',
581 'componentDidMount',
582 ]);
583
584 + log.length = 0;
585 await act(() => {
586 ReactNoop.render(
587 <React.StrictMode>
@@ -515,35 +590,38 @@ describe('StrictEffectsMode defaults', () => {
590 );
591 });
592
518 - assertLog(['componentDidUpdate']);
593 + expect(log).toEqual(['componentDidUpdate']);
594
595 + log.length = 0;
596 await act(() => {
597 ReactNoop.render(null);
598 });
599
524 - assertLog(['componentWillUnmount']);
600 + expect(log).toEqual(['componentWillUnmount']);
601 });
602
603 it('double flushing passive effects only results in one double invoke', async () => {
604 + const log = [];
605 function App({text}) {
606 const [state, setState] = React.useState(0);
607 React.useEffect(() => {
608 if (state !== 1) {
609 setState(1);
610 }
534 - Scheduler.log('useEffect mount');
535 - return () => Scheduler.log('useEffect unmount');
611 + log.push('useEffect mount');
612 + return () => log.push('useEffect unmount');
613 });
614
615 React.useLayoutEffect(() => {
539 - Scheduler.log('useLayoutEffect mount');
540 - return () => Scheduler.log('useLayoutEffect unmount');
616 + log.push('useLayoutEffect mount');
617 + return () => log.push('useLayoutEffect unmount');
618 });
619
543 - Scheduler.log(text);
620 + log.push(text);
621 return text;
622 }
623
624 + log.length = 0;
625 await act(() => {
626 ReactNoop.render(
627 <React.StrictMode>
@@ -552,7 +630,8 @@ describe('StrictEffectsMode defaults', () => {
630 );
631 });
632
555 - assertLog([
633 + expect(log).toEqual([
634 + 'mount',
635 'mount',
636 'useLayoutEffect mount',
637 'useEffect mount',
@@ -561,6 +640,7 @@ describe('StrictEffectsMode defaults', () => {
640 'useLayoutEffect mount',
641 'useEffect mount',
642 'mount',
643 + 'mount',
644 'useLayoutEffect unmount',
645 'useLayoutEffect mount',
646 'useEffect unmount',
@@ -570,14 +650,15 @@ describe('StrictEffectsMode defaults', () => {
650
651 it('newly mounted components after initial mount get double invoked', async () => {
652 let _setShowChild;
653 + const log = [];
654 function Child() {
655 React.useEffect(() => {
575 - Scheduler.log('Child useEffect mount');
576 - return () => Scheduler.log('Child useEffect unmount');
656 + log.push('Child useEffect mount');
657 + return () => log.push('Child useEffect unmount');
658 });
659 React.useLayoutEffect(() => {
579 - Scheduler.log('Child useLayoutEffect mount');
580 - return () => Scheduler.log('Child useLayoutEffect unmount');
660 + log.push('Child useLayoutEffect mount');
661 + return () => log.push('Child useLayoutEffect unmount');
662 });
663
664 return null;
@@ -587,12 +668,12 @@ describe('StrictEffectsMode defaults', () => {
668 const [showChild, setShowChild] = React.useState(false);
669 _setShowChild = setShowChild;
670 React.useEffect(() => {
590 - Scheduler.log('App useEffect mount');
591 - return () => Scheduler.log('App useEffect unmount');
671 + log.push('App useEffect mount');
672 + return () => log.push('App useEffect unmount');
673 });
674 React.useLayoutEffect(() => {
594 - Scheduler.log('App useLayoutEffect mount');
595 - return () => Scheduler.log('App useLayoutEffect unmount');
675 + log.push('App useLayoutEffect mount');
676 + return () => log.push('App useLayoutEffect unmount');
677 });
678
679 return showChild && <Child />;
@@ -606,7 +687,7 @@ describe('StrictEffectsMode defaults', () => {
687 );
688 });
689
609 - assertLog([
690 + expect(log).toEqual([
691 'App useLayoutEffect mount',
692 'App useEffect mount',
693 'App useLayoutEffect unmount',
@@ -615,11 +696,12 @@ describe('StrictEffectsMode defaults', () => {
696 'App useEffect mount',
697 ]);
698
699 + log.length = 0;
700 await act(() => {
701 _setShowChild(true);
702 });
703
622 - assertLog([
704 + expect(log).toEqual([
705 'App useLayoutEffect unmount',
706 'Child useLayoutEffect mount',
707 'App useLayoutEffect mount',
@@ -634,13 +716,14 @@ describe('StrictEffectsMode defaults', () => {
716 });
717
718 it('classes and functions are double invoked together correctly', async () => {
719 + const log = [];
720 class ClassChild extends React.PureComponent {
721 componentDidMount() {
639 - Scheduler.log('componentDidMount');
722 + log.push('componentDidMount');
723 }
724
725 componentWillUnmount() {
643 - Scheduler.log('componentWillUnmount');
726 + log.push('componentWillUnmount');
727 }
728
729 render() {
@@ -650,12 +733,12 @@ describe('StrictEffectsMode defaults', () => {
733
734 function FunctionChild({text}) {
735 React.useEffect(() => {
653 - Scheduler.log('useEffect mount');
654 - return () => Scheduler.log('useEffect unmount');
736 + log.push('useEffect mount');
737 + return () => log.push('useEffect unmount');
738 });
739 React.useLayoutEffect(() => {
657 - Scheduler.log('useLayoutEffect mount');
658 - return () => Scheduler.log('useLayoutEffect unmount');
740 + log.push('useLayoutEffect mount');
741 + return () => log.push('useLayoutEffect unmount');
742 });
743 return text;
744 }
@@ -677,7 +760,7 @@ describe('StrictEffectsMode defaults', () => {
760 );
761 });
762
680 - assertLog([
763 + expect(log).toEqual([
764 'componentDidMount',
765 'useLayoutEffect mount',
766 'useEffect mount',
@@ -689,6 +772,7 @@ describe('StrictEffectsMode defaults', () => {
772 'useEffect mount',
773 ]);
774
775 + log.length = 0;
776 await act(() => {
777 ReactNoop.render(
778 <React.StrictMode>
@@ -697,18 +781,19 @@ describe('StrictEffectsMode defaults', () => {
781 );
782 });
783
700 - assertLog([
784 + expect(log).toEqual([
785 'useLayoutEffect unmount',
786 'useLayoutEffect mount',
787 'useEffect unmount',
788 'useEffect mount',
789 ]);
790
791 + log.length = 0;
792 await act(() => {
793 ReactNoop.render(null);
794 });
795
711 - assertLog([
796 + expect(log).toEqual([
797 'componentWillUnmount',
798 'useLayoutEffect unmount',
799 'useEffect unmount',