main
js 1,033 lines 23.5 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 */
9
10 'use strict';
11
12 let React;
13 let ReactNoop;
14 let act;
15
16 describe('StrictEffectsMode', () => {
17 beforeEach(() => {
18 jest.resetModules();
19 act = require('internal-test-utils').act;
20
21 React = require('react');
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(() => {
30 log.push('useEffect mount');
31 return () => log.push('useEffect unmount');
32 });
33
34 React.useLayoutEffect(() => {
35 log.push('useLayoutEffect mount');
36 return () => log.push('useLayoutEffect unmount');
37 });
38
39 return text;
40 }
41
42 const root = ReactNoop.createLegacyRoot();
43 await act(() => {
44 root.render(
45 <React.StrictMode>
46 <App text={'mount'} />
47 </React.StrictMode>,
48 );
49 });
50
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(() => {
58 log.push('useEffect mount');
59 return () => log.push('useEffect unmount');
60 });
61
62 React.useLayoutEffect(() => {
63 log.push('useLayoutEffect mount');
64 return () => log.push('useLayoutEffect unmount');
65 });
66
67 return text;
68 }
69
70 await act(() => {
71 ReactNoop.renderToRootWithID(
72 <React.StrictMode>
73 <App text={'mount'} />
74 </React.StrictMode>,
75 'root',
76 );
77 });
78
79 if (__DEV__) {
80 expect(log).toEqual([
81 'useLayoutEffect mount',
82 'useEffect mount',
83 'useLayoutEffect unmount',
84 'useEffect unmount',
85 'useLayoutEffect mount',
86 'useEffect mount',
87 ]);
88 } else {
89 expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
90 }
91
92 log.length = 0;
93 await act(() => {
94 ReactNoop.renderToRootWithID(
95 <React.StrictMode>
96 <App text={'update'} />
97 </React.StrictMode>,
98 'root',
99 );
100 });
101
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
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 log.push('useEffect One mount');
122 return () => log.push('useEffect One unmount');
123 });
124
125 React.useEffect(() => {
126 log.push('useEffect Two mount');
127 return () => log.push('useEffect Two unmount');
128 });
129
130 return text;
131 }
132
133 await act(() => {
134 ReactNoop.renderToRootWithID(
135 <React.StrictMode>
136 <App text={'mount'} />
137 </React.StrictMode>,
138 'root',
139 );
140 });
141
142 if (__DEV__) {
143 expect(log).toEqual([
144 'useEffect One mount',
145 'useEffect Two mount',
146 'useEffect One unmount',
147 'useEffect Two unmount',
148 'useEffect One mount',
149 'useEffect Two mount',
150 ]);
151 } else {
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>
159 <App text={'update'} />
160 </React.StrictMode>,
161 'root',
162 );
163 });
164
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
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(() => {
184 log.push('useLayoutEffect One mount');
185 return () => log.push('useLayoutEffect One unmount');
186 });
187
188 React.useLayoutEffect(() => {
189 log.push('useLayoutEffect Two mount');
190 return () => log.push('useLayoutEffect Two unmount');
191 });
192
193 return text;
194 }
195
196 await act(() => {
197 ReactNoop.renderToRootWithID(
198 <React.StrictMode>
199 <App text={'mount'} />
200 </React.StrictMode>,
201 'root',
202 );
203 });
204
205 if (__DEV__) {
206 expect(log).toEqual([
207 'useLayoutEffect One mount',
208 'useLayoutEffect Two mount',
209 'useLayoutEffect One unmount',
210 'useLayoutEffect Two unmount',
211 'useLayoutEffect One mount',
212 'useLayoutEffect Two mount',
213 ]);
214 } else {
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>
225 <App text={'update'} />
226 </React.StrictMode>,
227 'root',
228 );
229 });
230
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
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(() => {
253 log.push('useEffect mount');
254 });
255
256 React.useLayoutEffect(() => {
257 log.push('useLayoutEffect mount');
258 });
259
260 return text;
261 }
262
263 await act(() => {
264 ReactNoop.renderToRootWithID(
265 <React.StrictMode>
266 <App text={'mount'} />
267 </React.StrictMode>,
268 );
269 });
270
271 if (__DEV__) {
272 expect(log).toEqual([
273 'useLayoutEffect mount',
274 'useEffect mount',
275 'useLayoutEffect mount',
276 'useEffect mount',
277 ]);
278 } else {
279 expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
280 }
281
282 log.length = 0;
283 await act(() => {
284 ReactNoop.renderToRootWithID(
285 <React.StrictMode>
286 <App text={'update'} />
287 </React.StrictMode>,
288 );
289 });
290
291 expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
292
293 log.length = 0;
294 await act(() => {
295 ReactNoop.unmountRootWithID('root');
296 });
297
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();
308 log.push('componentDidMount');
309 }
310
311 componentDidUpdate() {
312 this.test();
313 log.push('componentDidUpdate');
314 }
315
316 componentWillUnmount() {
317 this.test();
318 log.push('componentWillUnmount');
319 }
320
321 render() {
322 return null;
323 }
324 }
325
326 await act(() => {
327 ReactNoop.renderToRootWithID(
328 <React.StrictMode>
329 <App />
330 </React.StrictMode>,
331 );
332 });
333
334 if (__DEV__) {
335 expect(log).toEqual([
336 'componentDidMount',
337 'componentWillUnmount',
338 'componentDidMount',
339 ]);
340 } else {
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() {
349 log.push('componentDidMount');
350 }
351
352 componentDidUpdate() {
353 log.push('componentDidUpdate');
354 }
355
356 componentWillUnmount() {
357 log.push('componentWillUnmount');
358 }
359
360 render() {
361 return this.props.text;
362 }
363 }
364
365 await act(() => {
366 ReactNoop.renderToRootWithID(
367 <React.StrictMode>
368 <App text={'mount'} />
369 </React.StrictMode>,
370 'root',
371 );
372 });
373
374 if (__DEV__) {
375 expect(log).toEqual([
376 'componentDidMount',
377 'componentWillUnmount',
378 'componentDidMount',
379 ]);
380 } else {
381 expect(log).toEqual(['componentDidMount']);
382 }
383
384 log.length = 0;
385 await act(() => {
386 ReactNoop.renderToRootWithID(
387 <React.StrictMode>
388 <App text={'update'} />
389 </React.StrictMode>,
390 'root',
391 );
392 });
393
394 expect(log).toEqual(['componentDidUpdate']);
395
396 log.length = 0;
397 await act(() => {
398 ReactNoop.unmountRootWithID('root');
399 });
400
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() {
408 log.push('componentDidUpdate');
409 }
410
411 componentWillUnmount() {
412 log.push('componentWillUnmount');
413 }
414
415 render() {
416 return this.props.text;
417 }
418 }
419
420 await act(() => {
421 ReactNoop.renderToRootWithID(
422 <React.StrictMode>
423 <App text={'mount'} />
424 </React.StrictMode>,
425 'root',
426 );
427 });
428
429 if (__DEV__) {
430 expect(log).toEqual(['componentWillUnmount']);
431 } else {
432 expect(log).toEqual([]);
433 }
434
435 log.length = 0;
436 await act(() => {
437 ReactNoop.renderToRootWithID(
438 <React.StrictMode>
439 <App text={'update'} />
440 </React.StrictMode>,
441 'root',
442 );
443 });
444
445 expect(log).toEqual(['componentDidUpdate']);
446
447 log.length = 0;
448 await act(() => {
449 ReactNoop.unmountRootWithID('root');
450 });
451
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() {
460 log.push('componentDidMount');
461 }
462
463 componentDidUpdate() {
464 log.push('componentDidUpdate');
465 }
466
467 componentWillUnmount() {
468 log.push('componentWillUnmount');
469 }
470
471 render() {
472 return this.props.text;
473 }
474 }
475
476 const root = ReactNoop.createLegacyRoot();
477 await act(() => {
478 root.render(
479 <React.StrictMode>
480 <App text={'mount'} />
481 </React.StrictMode>,
482 );
483 });
484
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 }
496 log.push('useEffect mount');
497 return () => log.push('useEffect unmount');
498 });
499
500 React.useLayoutEffect(() => {
501 log.push('useLayoutEffect mount');
502 return () => log.push('useLayoutEffect unmount');
503 });
504
505 log.push(text);
506 return text;
507 }
508
509 await act(() => {
510 ReactNoop.renderToRootWithID(
511 <React.StrictMode>
512 <App text={'mount'} />
513 </React.StrictMode>,
514 );
515 });
516
517 if (__DEV__) {
518 expect(log).toEqual([
519 'mount',
520 'mount',
521 'useLayoutEffect mount',
522 'useEffect mount',
523 'useLayoutEffect unmount',
524 'useEffect unmount',
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 {
535 expect(log).toEqual([
536 'mount',
537 'useLayoutEffect mount',
538 'useEffect mount',
539 'mount',
540 'useLayoutEffect unmount',
541 'useLayoutEffect mount',
542 'useEffect unmount',
543 'useEffect mount',
544 ]);
545 }
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(() => {
553 log.push('Child useEffect mount');
554 return () => log.push('Child useEffect unmount');
555 });
556 React.useLayoutEffect(() => {
557 log.push('Child useLayoutEffect mount');
558 return () => log.push('Child useLayoutEffect unmount');
559 });
560
561 return null;
562 }
563
564 function App() {
565 const [showChild, setShowChild] = React.useState(false);
566 _setShowChild = setShowChild;
567 React.useEffect(() => {
568 log.push('App useEffect mount');
569 return () => log.push('App useEffect unmount');
570 });
571 React.useLayoutEffect(() => {
572 log.push('App useLayoutEffect mount');
573 return () => log.push('App useLayoutEffect unmount');
574 });
575
576 return showChild && <Child />;
577 }
578
579 await act(() => {
580 ReactNoop.renderToRootWithID(
581 <React.StrictMode>
582 <App />
583 </React.StrictMode>,
584 'root',
585 );
586 });
587
588 if (__DEV__) {
589 expect(log).toEqual([
590 'App useLayoutEffect mount',
591 'App useEffect mount',
592 'App useLayoutEffect unmount',
593 'App useEffect unmount',
594 'App useLayoutEffect mount',
595 'App useEffect mount',
596 ]);
597 } else {
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__) {
607 expect(log).toEqual([
608 'App useLayoutEffect unmount',
609 'Child useLayoutEffect mount',
610 'App useLayoutEffect mount',
611 'App useEffect unmount',
612 'Child useEffect mount',
613 'App useEffect mount',
614 'Child useLayoutEffect unmount',
615 'Child useEffect unmount',
616 'Child useLayoutEffect mount',
617 'Child useEffect mount',
618 ]);
619 } else {
620 expect(log).toEqual([
621 'App useLayoutEffect unmount',
622 'Child useLayoutEffect mount',
623 'App useLayoutEffect mount',
624 'App useEffect unmount',
625 'Child useEffect mount',
626 'App useEffect mount',
627 ]);
628 }
629 });
630
631 it('reordering keyed children does not re-run effects', async () => {
632 const log = [];
633 function Child({label}) {
634 React.useEffect(() => {
635 log.push(`${label} useEffect mount`);
636 return () => log.push(`${label} useEffect unmount`);
637 }, []);
638 React.useLayoutEffect(() => {
639 log.push(`${label} useLayoutEffect mount`);
640 return () => log.push(`${label} useLayoutEffect unmount`);
641 }, []);
642
643 return null;
644 }
645
646 function App({keys}) {
647 return keys.map(key => <Child key={key} label={key} />);
648 }
649
650 await act(() => {
651 ReactNoop.render(
652 <React.StrictMode>
653 <App keys={['a', 'b']} />
654 </React.StrictMode>,
655 );
656 });
657
658 if (__DEV__) {
659 expect(log).toEqual([
660 'a useLayoutEffect mount',
661 'b useLayoutEffect mount',
662 'a useEffect mount',
663 'b useEffect mount',
664 'a useLayoutEffect unmount',
665 'b useLayoutEffect unmount',
666 'a useEffect unmount',
667 'b useEffect unmount',
668 'a useLayoutEffect mount',
669 'b useLayoutEffect mount',
670 'a useEffect mount',
671 'b useEffect mount',
672 ]);
673 } else {
674 expect(log).toEqual([
675 'a useLayoutEffect mount',
676 'b useLayoutEffect mount',
677 'a useEffect mount',
678 'b useEffect mount',
679 ]);
680 }
681
682 // Reordering existing children must not re-run their effects. The
683 // components are neither unmounted nor remounted, and their effect
684 // dependencies have not changed.
685 log.length = 0;
686 await act(() => {
687 ReactNoop.render(
688 <React.StrictMode>
689 <App keys={['b', 'a']} />
690 </React.StrictMode>,
691 );
692 });
693
694 expect(log).toEqual([]);
695 });
696
697 it('classes and functions are double invoked together correctly', async () => {
698 const log = [];
699 class ClassChild extends React.PureComponent {
700 componentDidMount() {
701 log.push('componentDidMount');
702 }
703
704 componentWillUnmount() {
705 log.push('componentWillUnmount');
706 }
707
708 render() {
709 return this.props.text;
710 }
711 }
712
713 function FunctionChild({text}) {
714 React.useEffect(() => {
715 log.push('useEffect mount');
716 return () => log.push('useEffect unmount');
717 });
718 React.useLayoutEffect(() => {
719 log.push('useLayoutEffect mount');
720 return () => log.push('useLayoutEffect unmount');
721 });
722 return text;
723 }
724
725 function App({text}) {
726 return (
727 <>
728 <ClassChild text={text} />
729 <FunctionChild text={text} />
730 </>
731 );
732 }
733
734 await act(() => {
735 ReactNoop.renderToRootWithID(
736 <React.StrictMode>
737 <App text={'mount'} />
738 </React.StrictMode>,
739 'root',
740 );
741 });
742
743 if (__DEV__) {
744 expect(log).toEqual([
745 'componentDidMount',
746 'useLayoutEffect mount',
747 'useEffect mount',
748 'componentWillUnmount',
749 'useLayoutEffect unmount',
750 'useEffect unmount',
751 'componentDidMount',
752 'useLayoutEffect mount',
753 'useEffect mount',
754 ]);
755 } else {
756 expect(log).toEqual([
757 'componentDidMount',
758 'useLayoutEffect mount',
759 'useEffect mount',
760 ]);
761 }
762
763 log.length = 0;
764 await act(() => {
765 ReactNoop.renderToRootWithID(
766 <React.StrictMode>
767 <App text={'mount'} />
768 </React.StrictMode>,
769 'root',
770 );
771 });
772
773 expect(log).toEqual([
774 'useLayoutEffect unmount',
775 'useLayoutEffect mount',
776 'useEffect unmount',
777 'useEffect mount',
778 ]);
779
780 log.length = 0;
781 await act(() => {
782 ReactNoop.unmountRootWithID('root');
783 });
784
785 expect(log).toEqual([
786 'componentWillUnmount',
787 'useLayoutEffect unmount',
788 'useEffect unmount',
789 ]);
790 });
791
792 it('classes without componentDidMount and functions are double invoked together correctly', async () => {
793 const log = [];
794 class ClassChild extends React.PureComponent {
795 componentWillUnmount() {
796 log.push('componentWillUnmount');
797 }
798
799 render() {
800 return this.props.text;
801 }
802 }
803
804 function FunctionChild({text}) {
805 React.useEffect(() => {
806 log.push('useEffect mount');
807 return () => log.push('useEffect unmount');
808 });
809 React.useLayoutEffect(() => {
810 log.push('useLayoutEffect mount');
811 return () => log.push('useLayoutEffect unmount');
812 });
813 return text;
814 }
815
816 function App({text}) {
817 return (
818 <>
819 <ClassChild text={text} />
820 <FunctionChild text={text} />
821 </>
822 );
823 }
824
825 await act(() => {
826 ReactNoop.renderToRootWithID(
827 <React.StrictMode>
828 <App text={'mount'} />
829 </React.StrictMode>,
830 'root',
831 );
832 });
833
834 if (__DEV__) {
835 expect(log).toEqual([
836 'useLayoutEffect mount',
837 'useEffect mount',
838 'componentWillUnmount',
839 'useLayoutEffect unmount',
840 'useEffect unmount',
841 'useLayoutEffect mount',
842 'useEffect mount',
843 ]);
844 } else {
845 expect(log).toEqual(['useLayoutEffect mount', 'useEffect mount']);
846 }
847
848 log.length = 0;
849 await act(() => {
850 ReactNoop.renderToRootWithID(
851 <React.StrictMode>
852 <App text={'mount'} />
853 </React.StrictMode>,
854 'root',
855 );
856 });
857
858 expect(log).toEqual([
859 'useLayoutEffect unmount',
860 'useLayoutEffect mount',
861 'useEffect unmount',
862 'useEffect mount',
863 ]);
864
865 log.length = 0;
866 await act(() => {
867 ReactNoop.unmountRootWithID('root');
868 });
869
870 expect(log).toEqual([
871 'componentWillUnmount',
872 'useLayoutEffect unmount',
873 'useEffect unmount',
874 ]);
875 });
876
877 // @gate __DEV__
878 it('should double invoke effects after a re-suspend', async () => {
879 // Not using log.push because it silences double render logs.
880 let log = [];
881 let shouldSuspend = true;
882 let resolve;
883 const suspensePromise = new Promise(_resolve => {
884 resolve = _resolve;
885 });
886 function Fallback() {
887 log.push('Fallback');
888 return 'Loading';
889 }
890
891 function Parent({prop}) {
892 log.push('Parent rendered');
893
894 React.useEffect(() => {
895 log.push('Parent create');
896 return () => {
897 log.push('Parent destroy');
898 };
899 }, []);
900
901 React.useEffect(() => {
902 log.push('Parent dep create');
903 return () => {
904 log.push('Parent dep destroy');
905 };
906 }, [prop]);
907
908 return (
909 <React.Suspense fallback={<Fallback />}>
910 <Child prop={prop} />
911 </React.Suspense>
912 );
913 }
914
915 function Child({prop}) {
916 const [count, forceUpdate] = React.useState(0);
917 const ref = React.useRef(null);
918 log.push('Child rendered');
919 React.useEffect(() => {
920 log.push('Child create');
921 return () => {
922 log.push('Child destroy');
923 ref.current = true;
924 };
925 }, []);
926 const key = `${prop}-${count}`;
927 React.useEffect(() => {
928 log.push('Child dep create');
929 if (ref.current === true) {
930 ref.current = false;
931 forceUpdate(c => c + 1);
932 log.push('-----------------------after setState');
933 return;
934 }
935
936 return () => {
937 log.push('Child dep destroy');
938 };
939 }, [key]);
940
941 if (shouldSuspend) {
942 log.push('Child suspended');
943 throw suspensePromise;
944 }
945 return null;
946 }
947
948 // Initial mount
949 shouldSuspend = false;
950 await act(() => {
951 ReactNoop.render(
952 <React.StrictMode>
953 <Parent />
954 </React.StrictMode>,
955 );
956 });
957
958 // Now re-suspend
959 shouldSuspend = true;
960 log = [];
961 await act(() => {
962 ReactNoop.render(
963 <React.StrictMode>
964 <Parent />
965 </React.StrictMode>,
966 );
967 });
968
969 expect(log).toEqual([
970 'Parent rendered',
971 'Parent rendered',
972 'Child rendered',
973 'Child suspended',
974 'Fallback',
975 'Fallback',
976 // pre-warming
977 'Child rendered',
978 'Child suspended',
979 ]);
980
981 log = [];
982 // while suspended, update
983 await act(() => {
984 ReactNoop.render(
985 <React.StrictMode>
986 <Parent prop={'bar'} />
987 </React.StrictMode>,
988 );
989 });
990
991 expect(log).toEqual([
992 'Parent rendered',
993 'Parent rendered',
994 'Child rendered',
995 'Child suspended',
996 'Fallback',
997 'Fallback',
998 'Parent dep destroy',
999 'Parent dep create',
1000 // pre-warming
1001 'Child rendered',
1002 'Child suspended',
1003 ]);
1004
1005 log = [];
1006 // Now resolve and commit
1007 await act(() => {
1008 resolve();
1009 shouldSuspend = false;
1010 });
1011
1012 expect(log).toEqual([
1013 'Child rendered',
1014 'Child rendered',
1015 // !!! Committed, destroy and create effect.
1016 // !!! The other effect is not destroyed and created
1017 // !!! because the dep didn't change
1018 'Child dep destroy',
1019 'Child dep create',
1020
1021 // Double invoke both effects
1022 'Child destroy',
1023 'Child dep destroy',
1024 'Child create',
1025 'Child dep create',
1026 // Fires setState
1027 '-----------------------after setState',
1028 'Child rendered',
1029 'Child rendered',
1030 'Child dep create',
1031 ]);
1032 });
1033 });