main
js 1,030 lines 23 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @emails react-core
8 * @jest-environment node
9 */
10 'use strict';
11
12 let React;
13 let ReactNoop;
14 let waitForAll;
15 let assertConsoleErrorDev;
16
17 describe('ReactFragment', () => {
18 beforeEach(function () {
19 jest.resetModules();
20
21 React = require('react');
22 ReactNoop = require('react-noop-renderer');
23
24 const InternalTestUtils = require('internal-test-utils');
25 waitForAll = InternalTestUtils.waitForAll;
26 assertConsoleErrorDev = InternalTestUtils.assertConsoleErrorDev;
27 });
28
29 it('should render a single child via noop renderer', async () => {
30 const element = (
31 <>
32 <span>foo</span>
33 </>
34 );
35
36 ReactNoop.render(element);
37 await waitForAll([]);
38
39 expect(ReactNoop).toMatchRenderedOutput(<span>foo</span>);
40 });
41
42 it('should render zero children via noop renderer', async () => {
43 const element = <React.Fragment />;
44
45 ReactNoop.render(element);
46 await waitForAll([]);
47
48 expect(ReactNoop).toMatchRenderedOutput(null);
49 });
50
51 it('should render multiple children via noop renderer', async () => {
52 const element = (
53 <>
54 hello <span>world</span>
55 </>
56 );
57
58 ReactNoop.render(element);
59 await waitForAll([]);
60
61 expect(ReactNoop).toMatchRenderedOutput(
62 <>
63 hello <span>world</span>
64 </>,
65 );
66 });
67
68 it('should render an iterable via noop renderer', async () => {
69 const element = (
70 <>{new Set([<span key="a">hi</span>, <span key="b">bye</span>])}</>
71 );
72
73 ReactNoop.render(element);
74 await waitForAll([]);
75
76 expect(ReactNoop).toMatchRenderedOutput(
77 <>
78 <span>hi</span>
79 <span>bye</span>
80 </>,
81 );
82 });
83
84 it('should preserve state of children with 1 level nesting', async function () {
85 const ops = [];
86
87 class Stateful extends React.Component {
88 componentDidUpdate() {
89 ops.push('Update Stateful');
90 }
91
92 render() {
93 return <div>Hello</div>;
94 }
95 }
96
97 function Foo({condition}) {
98 return condition ? (
99 <Stateful key="a" />
100 ) : (
101 <>
102 <Stateful key="a" />
103 <div key="b">World</div>
104 </>
105 );
106 }
107
108 ReactNoop.render(<Foo condition={true} />);
109 await waitForAll([]);
110
111 ReactNoop.render(<Foo condition={false} />);
112 await waitForAll([]);
113
114 expect(ops).toEqual(['Update Stateful']);
115 expect(ReactNoop).toMatchRenderedOutput(
116 <>
117 <div>Hello</div>
118 <div>World</div>
119 </>,
120 );
121
122 ReactNoop.render(<Foo condition={true} />);
123 await waitForAll([]);
124
125 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
126 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
127 });
128
129 it('should preserve state between top-level fragments', async function () {
130 const ops = [];
131
132 class Stateful extends React.Component {
133 componentDidUpdate() {
134 ops.push('Update Stateful');
135 }
136
137 render() {
138 return <div>Hello</div>;
139 }
140 }
141
142 function Foo({condition}) {
143 return condition ? (
144 <>
145 <Stateful />
146 </>
147 ) : (
148 <>
149 <Stateful />
150 </>
151 );
152 }
153
154 ReactNoop.render(<Foo condition={true} />);
155 await waitForAll([]);
156
157 ReactNoop.render(<Foo condition={false} />);
158 await waitForAll([]);
159
160 expect(ops).toEqual(['Update Stateful']);
161 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
162
163 ReactNoop.render(<Foo condition={true} />);
164 await waitForAll([]);
165
166 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
167 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
168 });
169
170 it('should preserve state of children nested at same level', async function () {
171 const ops = [];
172
173 class Stateful extends React.Component {
174 componentDidUpdate() {
175 ops.push('Update Stateful');
176 }
177
178 render() {
179 return <div>Hello</div>;
180 }
181 }
182
183 function Foo({condition}) {
184 return condition ? (
185 <>
186 <>
187 <>
188 <Stateful key="a" />
189 </>
190 </>
191 </>
192 ) : (
193 <>
194 <>
195 <>
196 <div />
197 <Stateful key="a" />
198 </>
199 </>
200 </>
201 );
202 }
203
204 ReactNoop.render(<Foo condition={true} />);
205 await waitForAll([]);
206
207 ReactNoop.render(<Foo condition={false} />);
208 await waitForAll([]);
209
210 expect(ops).toEqual(['Update Stateful']);
211 expect(ReactNoop).toMatchRenderedOutput(
212 <>
213 <div />
214 <div>Hello</div>
215 </>,
216 );
217
218 ReactNoop.render(<Foo condition={true} />);
219 await waitForAll([]);
220
221 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
222 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
223 });
224
225 it('should not preserve state in non-top-level fragment nesting', async function () {
226 const ops = [];
227
228 class Stateful extends React.Component {
229 componentDidUpdate() {
230 ops.push('Update Stateful');
231 }
232
233 render() {
234 return <div>Hello</div>;
235 }
236 }
237
238 function Foo({condition}) {
239 return condition ? (
240 <>
241 <>
242 <Stateful key="a" />
243 </>
244 </>
245 ) : (
246 <>
247 <Stateful key="a" />
248 </>
249 );
250 }
251
252 ReactNoop.render(<Foo condition={true} />);
253 await waitForAll([]);
254
255 ReactNoop.render(<Foo condition={false} />);
256 await waitForAll([]);
257
258 expect(ops).toEqual([]);
259 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
260
261 ReactNoop.render(<Foo condition={true} />);
262 await waitForAll([]);
263
264 expect(ops).toEqual([]);
265 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
266 });
267
268 it('should not preserve state of children if nested 2 levels without siblings', async function () {
269 const ops = [];
270
271 class Stateful extends React.Component {
272 componentDidUpdate() {
273 ops.push('Update Stateful');
274 }
275
276 render() {
277 return <div>Hello</div>;
278 }
279 }
280
281 function Foo({condition}) {
282 return condition ? (
283 <Stateful key="a" />
284 ) : (
285 <>
286 <>
287 <Stateful key="a" />
288 </>
289 </>
290 );
291 }
292
293 ReactNoop.render(<Foo condition={true} />);
294 await waitForAll([]);
295
296 ReactNoop.render(<Foo condition={false} />);
297 await waitForAll([]);
298
299 expect(ops).toEqual([]);
300 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
301
302 ReactNoop.render(<Foo condition={true} />);
303 await waitForAll([]);
304
305 expect(ops).toEqual([]);
306 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
307 });
308
309 it('should not preserve state of children if nested 2 levels with siblings', async function () {
310 const ops = [];
311
312 class Stateful extends React.Component {
313 componentDidUpdate() {
314 ops.push('Update Stateful');
315 }
316
317 render() {
318 return <div>Hello</div>;
319 }
320 }
321
322 function Foo({condition}) {
323 return condition ? (
324 <Stateful key="a" />
325 ) : (
326 <>
327 <>
328 <Stateful key="a" />
329 </>
330 <div />
331 </>
332 );
333 }
334
335 ReactNoop.render(<Foo condition={true} />);
336 await waitForAll([]);
337
338 ReactNoop.render(<Foo condition={false} />);
339 await waitForAll([]);
340
341 expect(ops).toEqual([]);
342 expect(ReactNoop).toMatchRenderedOutput(
343 <>
344 <div>Hello</div>
345 <div />
346 </>,
347 );
348
349 ReactNoop.render(<Foo condition={true} />);
350 await waitForAll([]);
351
352 expect(ops).toEqual([]);
353 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
354 });
355
356 it('should preserve state between array nested in fragment and fragment', async function () {
357 const ops = [];
358
359 class Stateful extends React.Component {
360 componentDidUpdate() {
361 ops.push('Update Stateful');
362 }
363
364 render() {
365 return <div>Hello</div>;
366 }
367 }
368
369 function Foo({condition}) {
370 return condition ? (
371 <>
372 <Stateful key="a" />
373 </>
374 ) : (
375 <>{[<Stateful key="a" />]}</>
376 );
377 }
378
379 ReactNoop.render(<Foo condition={true} />);
380 await waitForAll([]);
381
382 ReactNoop.render(<Foo condition={false} />);
383 await waitForAll([]);
384
385 expect(ops).toEqual(['Update Stateful']);
386 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
387
388 ReactNoop.render(<Foo condition={true} />);
389 await waitForAll([]);
390
391 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
392 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
393 });
394
395 it('should preserve state between top level fragment and array', async function () {
396 const ops = [];
397
398 class Stateful extends React.Component {
399 componentDidUpdate() {
400 ops.push('Update Stateful');
401 }
402
403 render() {
404 return <div>Hello</div>;
405 }
406 }
407
408 function Foo({condition}) {
409 return condition ? (
410 [<Stateful key="a" />]
411 ) : (
412 <>
413 <Stateful key="a" />
414 </>
415 );
416 }
417
418 ReactNoop.render(<Foo condition={true} />);
419 await waitForAll([]);
420
421 ReactNoop.render(<Foo condition={false} />);
422 await waitForAll([]);
423
424 expect(ops).toEqual(['Update Stateful']);
425 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
426
427 ReactNoop.render(<Foo condition={true} />);
428 await waitForAll([]);
429
430 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
431 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
432 });
433
434 it('should not preserve state between array nested in fragment and double nested fragment', async function () {
435 const ops = [];
436
437 class Stateful extends React.Component {
438 componentDidUpdate() {
439 ops.push('Update Stateful');
440 }
441
442 render() {
443 return <div>Hello</div>;
444 }
445 }
446
447 function Foo({condition}) {
448 return condition ? (
449 <>{[<Stateful key="a" />]}</>
450 ) : (
451 <>
452 <>
453 <Stateful key="a" />
454 </>
455 </>
456 );
457 }
458
459 ReactNoop.render(<Foo condition={true} />);
460 await waitForAll([]);
461
462 ReactNoop.render(<Foo condition={false} />);
463 await waitForAll([]);
464
465 expect(ops).toEqual([]);
466 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
467
468 ReactNoop.render(<Foo condition={true} />);
469 await waitForAll([]);
470
471 expect(ops).toEqual([]);
472 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
473 });
474
475 it('should not preserve state between array nested in fragment and double nested array', async function () {
476 const ops = [];
477
478 class Stateful extends React.Component {
479 componentDidUpdate() {
480 ops.push('Update Stateful');
481 }
482
483 render() {
484 return <div>Hello</div>;
485 }
486 }
487
488 function Foo({condition}) {
489 return condition ? (
490 <>{[<Stateful key="a" />]}</>
491 ) : (
492 [[<Stateful key="a" />]]
493 );
494 }
495
496 ReactNoop.render(<Foo condition={true} />);
497 await waitForAll([]);
498
499 ReactNoop.render(<Foo condition={false} />);
500 await waitForAll([]);
501
502 expect(ops).toEqual([]);
503 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
504
505 ReactNoop.render(<Foo condition={true} />);
506 await waitForAll([]);
507
508 expect(ops).toEqual([]);
509 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
510 });
511
512 it('should preserve state between double nested fragment and double nested array', async function () {
513 const ops = [];
514
515 class Stateful extends React.Component {
516 componentDidUpdate() {
517 ops.push('Update Stateful');
518 }
519
520 render() {
521 return <div>Hello</div>;
522 }
523 }
524
525 function Foo({condition}) {
526 return condition ? (
527 <>
528 <>
529 <Stateful key="a" />
530 </>
531 </>
532 ) : (
533 [[<Stateful key="a" />]]
534 );
535 }
536
537 ReactNoop.render(<Foo condition={true} />);
538 await waitForAll([]);
539
540 ReactNoop.render(<Foo condition={false} />);
541 await waitForAll([]);
542
543 expect(ops).toEqual(['Update Stateful']);
544 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
545
546 ReactNoop.render(<Foo condition={true} />);
547 await waitForAll([]);
548
549 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
550 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
551 });
552
553 it('should not preserve state of children when the keys are different', async function () {
554 const ops = [];
555
556 class Stateful extends React.Component {
557 componentDidUpdate() {
558 ops.push('Update Stateful');
559 }
560
561 render() {
562 return <div>Hello</div>;
563 }
564 }
565
566 function Foo({condition}) {
567 return condition ? (
568 <React.Fragment key="a">
569 <Stateful />
570 </React.Fragment>
571 ) : (
572 <React.Fragment key="b">
573 <Stateful />
574 <span>World</span>
575 </React.Fragment>
576 );
577 }
578
579 ReactNoop.render(<Foo condition={true} />);
580 await waitForAll([]);
581
582 ReactNoop.render(<Foo condition={false} />);
583 await waitForAll([]);
584
585 expect(ops).toEqual([]);
586 expect(ReactNoop).toMatchRenderedOutput(
587 <>
588 <div>Hello</div>
589 <span>World</span>
590 </>,
591 );
592
593 ReactNoop.render(<Foo condition={true} />);
594 await waitForAll([]);
595
596 expect(ops).toEqual([]);
597 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
598 });
599
600 it('should not preserve state between unkeyed and keyed fragment', async function () {
601 const ops = [];
602
603 class Stateful extends React.Component {
604 componentDidUpdate() {
605 ops.push('Update Stateful');
606 }
607
608 render() {
609 return <div>Hello</div>;
610 }
611 }
612
613 function Foo({condition}) {
614 return condition ? (
615 <React.Fragment key="a">
616 <Stateful />
617 </React.Fragment>
618 ) : (
619 <>
620 <Stateful />
621 </>
622 );
623 }
624
625 ReactNoop.render(<Foo condition={true} />);
626 await waitForAll([]);
627
628 ReactNoop.render(<Foo condition={false} />);
629 await waitForAll([]);
630
631 expect(ops).toEqual([]);
632 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
633
634 ReactNoop.render(<Foo condition={true} />);
635 await waitForAll([]);
636
637 expect(ops).toEqual([]);
638 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
639 });
640
641 it('should preserve state with reordering in multiple levels', async function () {
642 const ops = [];
643
644 class Stateful extends React.Component {
645 componentDidUpdate() {
646 ops.push('Update Stateful');
647 }
648
649 render() {
650 return <div>Hello</div>;
651 }
652 }
653
654 function Foo({condition}) {
655 return condition ? (
656 <div>
657 <React.Fragment key="c">
658 <span>foo</span>
659 <div key="b">
660 <Stateful key="a" />
661 </div>
662 </React.Fragment>
663 <span>boop</span>
664 </div>
665 ) : (
666 <div>
667 <span>beep</span>
668 <React.Fragment key="c">
669 <div key="b">
670 <Stateful key="a" />
671 </div>
672 <span>bar</span>
673 </React.Fragment>
674 </div>
675 );
676 }
677
678 ReactNoop.render(<Foo condition={true} />);
679 await waitForAll([]);
680
681 ReactNoop.render(<Foo condition={false} />);
682 await waitForAll([]);
683
684 expect(ops).toEqual(['Update Stateful']);
685 expect(ReactNoop).toMatchRenderedOutput(
686 <div>
687 <span>beep</span>
688 <div>
689 <div>Hello</div>
690 </div>
691 <span>bar</span>
692 </div>,
693 );
694
695 ReactNoop.render(<Foo condition={true} />);
696 await waitForAll([]);
697
698 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
699 expect(ReactNoop).toMatchRenderedOutput(
700 <div>
701 <span>foo</span>
702 <div>
703 <div>Hello</div>
704 </div>
705 <span>boop</span>
706 </div>,
707 );
708 });
709
710 it('should not preserve state when switching to a keyed fragment to an array', async () => {
711 const ops = [];
712
713 class Stateful extends React.Component {
714 componentDidUpdate() {
715 ops.push('Update Stateful');
716 }
717
718 render() {
719 return <div>Hello</div>;
720 }
721 }
722
723 function Foo({condition}) {
724 return condition ? (
725 <div>
726 {
727 <React.Fragment key="foo">
728 <Stateful />
729 </React.Fragment>
730 }
731 <span />
732 </div>
733 ) : (
734 <div>
735 {[<Stateful />]}
736 <span />
737 </div>
738 );
739 }
740
741 ReactNoop.render(<Foo condition={true} />);
742 await waitForAll([]);
743
744 ReactNoop.render(<Foo condition={false} />);
745 await waitForAll([]);
746 assertConsoleErrorDev([
747 'Each child in a list should have a unique "key" prop.\n' +
748 '\n' +
749 'Check the render method of `div`. ' +
750 'It was passed a child from Foo. ' +
751 'See https://react.dev/link/warning-keys for more information.\n' +
752 ' in Foo (at **)',
753 ]);
754
755 expect(ops).toEqual([]);
756 expect(ReactNoop).toMatchRenderedOutput(
757 <div>
758 <div>Hello</div>
759 <span />
760 </div>,
761 );
762
763 ReactNoop.render(<Foo condition={true} />);
764 await waitForAll([]);
765
766 expect(ops).toEqual([]);
767 expect(ReactNoop).toMatchRenderedOutput(
768 <div>
769 <div>Hello</div>
770 <span />
771 </div>,
772 );
773 });
774
775 it('should not preserve state when switching a nested unkeyed fragment to a passthrough component', async function () {
776 const ops = [];
777
778 function Passthrough({children}) {
779 return children;
780 }
781
782 class Stateful extends React.Component {
783 componentDidUpdate() {
784 ops.push('Update Stateful');
785 }
786
787 render() {
788 return <div>Hello</div>;
789 }
790 }
791
792 function Foo({condition}) {
793 return condition ? (
794 <>
795 <>
796 <Stateful />
797 </>
798 </>
799 ) : (
800 <>
801 <Passthrough>
802 <Stateful />
803 </Passthrough>
804 </>
805 );
806 }
807
808 ReactNoop.render(<Foo condition={true} />);
809 await waitForAll([]);
810
811 ReactNoop.render(<Foo condition={false} />);
812 await waitForAll([]);
813
814 expect(ops).toEqual([]);
815 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
816
817 ReactNoop.render(<Foo condition={true} />);
818 await waitForAll([]);
819
820 expect(ops).toEqual([]);
821 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
822 });
823
824 it('should not preserve state when switching a nested keyed fragment to a passthrough component', async function () {
825 const ops = [];
826
827 function Passthrough({children}) {
828 return children;
829 }
830
831 class Stateful extends React.Component {
832 componentDidUpdate() {
833 ops.push('Update Stateful');
834 }
835
836 render() {
837 return <div>Hello</div>;
838 }
839 }
840
841 function Foo({condition}) {
842 return condition ? (
843 <>
844 <React.Fragment key="a">
845 <Stateful />
846 </React.Fragment>
847 </>
848 ) : (
849 <>
850 <Passthrough>
851 <Stateful />
852 </Passthrough>
853 </>
854 );
855 }
856
857 ReactNoop.render(<Foo condition={true} />);
858 await waitForAll([]);
859
860 ReactNoop.render(<Foo condition={false} />);
861 await waitForAll([]);
862
863 expect(ops).toEqual([]);
864 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
865
866 ReactNoop.render(<Foo condition={true} />);
867 await waitForAll([]);
868
869 expect(ops).toEqual([]);
870 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
871 });
872
873 it('should not preserve state when switching a nested keyed array to a passthrough component', async function () {
874 const ops = [];
875
876 function Passthrough({children}) {
877 return children;
878 }
879
880 class Stateful extends React.Component {
881 componentDidUpdate() {
882 ops.push('Update Stateful');
883 }
884
885 render() {
886 return <div>Hello</div>;
887 }
888 }
889
890 function Foo({condition}) {
891 return condition ? (
892 <>{[<Stateful key="a" />]}</>
893 ) : (
894 <>
895 <Passthrough>
896 <Stateful />
897 </Passthrough>
898 </>
899 );
900 }
901
902 ReactNoop.render(<Foo condition={true} />);
903 await waitForAll([]);
904
905 ReactNoop.render(<Foo condition={false} />);
906 await waitForAll([]);
907
908 expect(ops).toEqual([]);
909 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
910
911 ReactNoop.render(<Foo condition={true} />);
912 await waitForAll([]);
913
914 expect(ops).toEqual([]);
915 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
916 });
917
918 it('should preserve state when it does not change positions', async function () {
919 const ops = [];
920
921 class Stateful extends React.Component {
922 componentDidUpdate() {
923 ops.push('Update Stateful');
924 }
925
926 render() {
927 return <div>Hello</div>;
928 }
929 }
930
931 function Foo({condition}) {
932 return condition
933 ? [
934 <span />,
935 <>
936 <Stateful />
937 </>,
938 ]
939 : [
940 <span />,
941 <>
942 <Stateful />
943 </>,
944 ];
945 }
946
947 ReactNoop.render(<Foo condition={true} />);
948 await waitForAll([]);
949 assertConsoleErrorDev([
950 'Each child in a list should have a unique "key" prop.\n' +
951 '\n' +
952 'Check the top-level render call using <Foo>. ' +
953 'It was passed a child from Foo. ' +
954 'See https://react.dev/link/warning-keys for more information.\n' +
955 ' in span (at **)\n' +
956 ' in Foo (at **)',
957 ]);
958
959 ReactNoop.render(<Foo condition={false} />);
960 // The key warning gets deduped because it's in the same component.
961 await waitForAll([]);
962
963 expect(ops).toEqual(['Update Stateful']);
964 expect(ReactNoop).toMatchRenderedOutput(
965 <>
966 <span />
967 <div>Hello</div>
968 </>,
969 );
970
971 ReactNoop.render(<Foo condition={true} />);
972 // The key warning gets deduped because it's in the same component.
973 await waitForAll([]);
974
975 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
976 expect(ReactNoop).toMatchRenderedOutput(
977 <>
978 <span />
979 <div>Hello</div>
980 </>,
981 );
982 });
983
984 it('should preserve state of children when adding a fragment wrapped in Lazy', async function () {
985 const ops = [];
986
987 class Stateful extends React.Component {
988 componentDidUpdate() {
989 ops.push('Update Stateful');
990 }
991
992 render() {
993 return <div>Hello</div>;
994 }
995 }
996
997 const lazyChild = React.lazy(async () => ({
998 default: (
999 <>
1000 <Stateful key="a" />
1001 <div key="b">World</div>
1002 </>
1003 ),
1004 }));
1005
1006 function Foo({condition}) {
1007 return condition ? <Stateful key="a" /> : lazyChild;
1008 }
1009
1010 ReactNoop.render(<Foo condition={true} />);
1011 await waitForAll([]);
1012
1013 ReactNoop.render(<Foo condition={false} />);
1014 await waitForAll([]);
1015
1016 expect(ops).toEqual(['Update Stateful']);
1017 expect(ReactNoop).toMatchRenderedOutput(
1018 <>
1019 <div>Hello</div>
1020 <div>World</div>
1021 </>,
1022 );
1023
1024 ReactNoop.render(<Foo condition={true} />);
1025 await waitForAll([]);
1026
1027 expect(ops).toEqual(['Update Stateful', 'Update Stateful']);
1028 expect(ReactNoop).toMatchRenderedOutput(<div>Hello</div>);
1029 });
1030 });