@samitouri / QOS-React-2 / commits / 2594caa09e

Convert ReactComponentLifeCycle-test to createRoot (#27916)

Convert ReactComponentLifeCycle-test to createRoot

Jan Kassens committed Jan 10, 2024 at 10:16 UTC 2594caa09e0aca49493cd62659c1e3d1d3488a7e
1 file changed +298 -159
packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+298 -159
@@ -9,8 +9,11 @@
9
10 'use strict';
11
12 +let act;
13 +
14 let React;
15 let ReactDOM;
16 +let ReactDOMClient;
17 let ReactTestUtils;
18 let PropTypes;
19
@@ -88,13 +91,17 @@ function getLifeCycleState(instance): ComponentLifeCycle {
91 describe('ReactComponentLifeCycle', () => {
92 beforeEach(() => {
93 jest.resetModules();
94 +
95 + act = require('internal-test-utils').act;
96 +
97 React = require('react');
98 ReactDOM = require('react-dom');
99 + ReactDOMClient = require('react-dom/client');
100 ReactTestUtils = require('react-dom/test-utils');
101 PropTypes = require('prop-types');
102 });
103
97 - it('should not reuse an instance when it has been unmounted', () => {
104 + it('should not reuse an instance when it has been unmounted', async () => {
105 const container = document.createElement('div');
106
107 class StatefulComponent extends React.Component {
@@ -299,21 +306,27 @@ describe('ReactComponentLifeCycle', () => {
306 }).toErrorDev('Component is accessing isMounted inside its render()');
307 });
308
302 - it('isMounted should return false when unmounted', () => {
309 + it('isMounted should return false when unmounted', async () => {
310 class Component extends React.Component {
311 render() {
312 return <div />;
313 }
314 }
315
309 - const container = document.createElement('div');
310 - const instance = ReactDOM.render(<Component />, container);
316 + const root = ReactDOMClient.createRoot(document.createElement('div'));
317 + const instanceRef = React.createRef();
318 + await act(() => {
319 + root.render(<Component ref={instanceRef} />);
320 + });
321 + const instance = instanceRef.current;
322
323 // No longer a public API, but we can test that it works internally by
324 // reaching into the updater.
325 expect(instance.updater.isMounted(instance)).toBe(true);
326
316 - ReactDOM.unmountComponentAtNode(container);
327 + await act(() => {
328 + root.unmount();
329 + });
330
331 expect(instance.updater.isMounted(instance)).toBe(false);
332 });
@@ -337,7 +350,7 @@ describe('ReactComponentLifeCycle', () => {
350 }).toErrorDev('Component is accessing findDOMNode inside its render()');
351 });
352
340 - it('should carry through each of the phases of setup', () => {
353 + it('should carry through each of the phases of setup', async () => {
354 class LifeCycleComponent extends React.Component {
355 constructor(props, context) {
356 super(props, context);
@@ -390,15 +403,17 @@ describe('ReactComponentLifeCycle', () => {
403
404 // A component that is merely "constructed" (as in "constructor") but not
405 // yet initialized, or rendered.
393 - //
394 - const container = document.createElement('div');
406 + const root = ReactDOMClient.createRoot(document.createElement('div'));
407
396 - let instance;
397 - expect(() => {
398 - instance = ReactDOM.render(<LifeCycleComponent />, container);
408 + const instanceRef = React.createRef();
409 + await expect(async () => {
410 + await act(() => {
411 + root.render(<LifeCycleComponent ref={instanceRef} />);
412 + });
413 }).toErrorDev(
414 'LifeCycleComponent is accessing isMounted inside its render() function',
415 );
416 + const instance = instanceRef.current;
417
418 // getInitialState
419 expect(instance._testJournal.returnedFromGetInitialState).toEqual(
@@ -437,7 +452,9 @@ describe('ReactComponentLifeCycle', () => {
452
453 expect(getLifeCycleState(instance)).toBe('MOUNTED');
454
440 - ReactDOM.unmountComponentAtNode(container);
455 + await act(() => {
456 + root.unmount();
457 + });
458
459 expect(instance._testJournal.stateAtStartOfWillUnmount).toEqual(
460 WILL_UNMOUNT_STATE,
@@ -450,14 +467,15 @@ describe('ReactComponentLifeCycle', () => {
467 expect(instance.state).toEqual(POST_WILL_UNMOUNT_STATE);
468 });
469
453 - it('should not throw when updating an auxiliary component', () => {
470 + it('should not throw when updating an auxiliary component', async () => {
471 class Tooltip extends React.Component {
472 render() {
473 return <div>{this.props.children}</div>;
474 }
475
476 componentDidMount() {
460 - this.container = document.createElement('div');
477 + const container = document.createElement('div');
478 + this.root = ReactDOMClient.createRoot(container);
479 this.updateTooltip();
480 }
481
@@ -468,7 +486,7 @@ describe('ReactComponentLifeCycle', () => {
486 updateTooltip = () => {
487 // Even though this.props.tooltip has an owner, updating it shouldn't
488 // throw here because it's mounted as a root component
471 - ReactDOM.render(this.props.tooltip, this.container);
489 + this.root.render(this.props.tooltip, this.container);
490 };
491 }
492
@@ -484,12 +502,16 @@ describe('ReactComponentLifeCycle', () => {
502 }
503 }
504
487 - const container = document.createElement('div');
488 - ReactDOM.render(<Component text="uno" tooltipText="one" />, container);
505 + const root = ReactDOMClient.createRoot(document.createElement('div'));
506 + await act(() => {
507 + root.render(<Component text="uno" tooltipText="one" />);
508 + });
509
510 // Since `instance` is a root component, we can set its props. This also
511 // makes Tooltip rerender the tooltip component, which shouldn't throw.
492 - ReactDOM.render(<Component text="dos" tooltipText="two" />, container);
512 + await act(() => {
513 + root.render(<Component text="dos" tooltipText="two" />);
514 + });
515 });
516
517 it('should allow state updates in componentDidMount', () => {
@@ -520,7 +542,7 @@ describe('ReactComponentLifeCycle', () => {
542 expect(instance.state.stateField).toBe('goodbye');
543 });
544
523 - it('should call nested legacy lifecycle methods in the right order', () => {
545 + it('should call nested legacy lifecycle methods in the right order', async () => {
546 let log;
547 const logger = function (msg) {
548 return function () {
@@ -563,9 +585,11 @@ describe('ReactComponentLifeCycle', () => {
585 }
586 }
587
566 - const container = document.createElement('div');
588 + const root = ReactDOMClient.createRoot(document.createElement('div'));
589 log = [];
568 - ReactDOM.render(<Outer x={1} />, container);
590 + await act(() => {
591 + root.render(<Outer x={1} />);
592 + });
593 expect(log).toEqual([
594 'outer componentWillMount',
595 'inner componentWillMount',
@@ -575,7 +599,9 @@ describe('ReactComponentLifeCycle', () => {
599
600 // Dedup warnings
601 log = [];
578 - ReactDOM.render(<Outer x={2} />, container);
602 + await act(() => {
603 + root.render(<Outer x={2} />);
604 + });
605 expect(log).toEqual([
606 'outer componentWillReceiveProps',
607 'outer shouldComponentUpdate',
@@ -588,14 +614,16 @@ describe('ReactComponentLifeCycle', () => {
614 ]);
615
616 log = [];
591 - ReactDOM.unmountComponentAtNode(container);
617 + await act(() => {
618 + root.unmount();
619 + });
620 expect(log).toEqual([
621 'outer componentWillUnmount',
622 'inner componentWillUnmount',
623 ]);
624 });
625
598 - it('should call nested new lifecycle methods in the right order', () => {
626 + it('should call nested new lifecycle methods in the right order', async () => {
627 let log;
628 const logger = function (msg) {
629 return function () {
@@ -640,9 +668,12 @@ describe('ReactComponentLifeCycle', () => {
668 }
669 }
670
643 - const container = document.createElement('div');
671 + const root = ReactDOMClient.createRoot(document.createElement('div'));
672 +
673 log = [];
645 - ReactDOM.render(<Outer x={1} />, container);
674 + await act(() => {
675 + root.render(<Outer x={1} />);
676 + });
677 expect(log).toEqual([
678 'outer getDerivedStateFromProps',
679 'inner getDerivedStateFromProps',
@@ -652,7 +683,9 @@ describe('ReactComponentLifeCycle', () => {
683
684 // Dedup warnings
685 log = [];
655 - ReactDOM.render(<Outer x={2} />, container);
686 + await act(() => {
687 + root.render(<Outer x={2} />);
688 + });
689 expect(log).toEqual([
690 'outer getDerivedStateFromProps',
691 'outer shouldComponentUpdate',
@@ -665,14 +698,16 @@ describe('ReactComponentLifeCycle', () => {
698 ]);
699
700 log = [];
668 - ReactDOM.unmountComponentAtNode(container);
701 + await act(() => {
702 + root.unmount();
703 + });
704 expect(log).toEqual([
705 'outer componentWillUnmount',
706 'inner componentWillUnmount',
707 ]);
708 });
709
675 - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
710 + it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', async () => {
711 class Component extends React.Component {
712 state = {};
713 static getDerivedStateFromProps() {
@@ -692,9 +727,13 @@ describe('ReactComponentLifeCycle', () => {
727 }
728 }
729
695 - const container = document.createElement('div');
696 - expect(() => {
697 - expect(() => ReactDOM.render(<Component />, container)).toErrorDev(
730 + const root = ReactDOMClient.createRoot(document.createElement('div'));
731 + await expect(async () => {
732 + await expect(async () => {
733 + await act(() => {
734 + root.render(<Component />);
735 + });
736 + }).toErrorDev(
737 'Unsafe legacy lifecycles will not be called for components using new component APIs.',
738 );
739 }).toWarnDev(
@@ -707,7 +746,7 @@ describe('ReactComponentLifeCycle', () => {
746 );
747 });
748
710 - it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => {
749 + it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', async () => {
750 class Component extends React.Component {
751 state = {};
752 getSnapshotBeforeUpdate() {
@@ -728,10 +767,13 @@ describe('ReactComponentLifeCycle', () => {
767 }
768 }
769
731 - const container = document.createElement('div');
732 - expect(() => {
733 - expect(() =>
734 - ReactDOM.render(<Component value={1} />, container),
770 + const root = ReactDOMClient.createRoot(document.createElement('div'));
771 + await expect(async () => {
772 + await expect(
773 + async () =>
774 + await act(() => {
775 + root.render(<Component value={1} />);
776 + }),
777 ).toErrorDev(
778 'Unsafe legacy lifecycles will not be called for components using new component APIs.',
779 );
@@ -743,10 +785,13 @@ describe('ReactComponentLifeCycle', () => {
785 ],
786 {withoutStack: true},
787 );
746 - ReactDOM.render(<Component value={2} />, container);
788 +
789 + await act(() => {
790 + root.render(<Component value={2} />);
791 + });
792 });
793
749 - it('should not invoke new unsafe lifecycles (cWM/cWRP/cWU) if static gDSFP is present', () => {
794 + it('should not invoke new unsafe lifecycles (cWM/cWRP/cWU) if static gDSFP is present', async () => {
795 class Component extends React.Component {
796 state = {};
797 static getDerivedStateFromProps() {
@@ -766,18 +811,21 @@ describe('ReactComponentLifeCycle', () => {
811 }
812 }
813
769 - const container = document.createElement('div');
770 - expect(() =>
771 - ReactDOM.render(<Component value={1} />, container),
814 + const root = ReactDOMClient.createRoot(document.createElement('div'));
815 + await expect(
816 + async () =>
817 + await act(() => {
818 + root.render(<Component value={1} />);
819 + }),
820 ).toErrorDev(
821 'Unsafe legacy lifecycles will not be called for components using new component APIs.',
822 );
775 - ReactDOM.render(<Component value={2} />, container);
823 + await act(() => {
824 + root.render(<Component value={2} />);
825 + });
826 });
827
778 - it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
779 - const container = document.createElement('div');
780 -
828 + it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', async () => {
829 class AllLegacyLifecycles extends React.Component {
830 state = {};
831 static getDerivedStateFromProps() {
@@ -791,10 +839,13 @@ describe('ReactComponentLifeCycle', () => {
839 }
840 }
841
794 - expect(() => {
795 - expect(() =>
796 - ReactDOM.render(<AllLegacyLifecycles />, container),
797 - ).toErrorDev(
842 + const root = ReactDOMClient.createRoot(document.createElement('div'));
843 + await expect(async () => {
844 + await expect(async () => {
845 + await act(() => {
846 + root.render(<AllLegacyLifecycles />);
847 + });
848 + }).toErrorDev(
849 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
850 'AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
851 ' componentWillMount\n' +
@@ -822,7 +873,11 @@ describe('ReactComponentLifeCycle', () => {
873 }
874 }
875
825 - expect(() => ReactDOM.render(<WillMount />, container)).toErrorDev(
876 + await expect(async () => {
877 + await act(() => {
878 + root.render(<WillMount />);
879 + });
880 + }).toErrorDev(
881 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
882 'WillMount uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
883 ' UNSAFE_componentWillMount\n\n' +
@@ -842,9 +897,12 @@ describe('ReactComponentLifeCycle', () => {
897 }
898 }
899
845 - expect(() => {
846 - expect(() =>
847 - ReactDOM.render(<WillMountAndUpdate />, container),
900 + await expect(async () => {
901 + await expect(
902 + async () =>
903 + await act(() => {
904 + root.render(<WillMountAndUpdate />);
905 + }),
906 ).toErrorDev(
907 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
908 'WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
@@ -868,8 +926,12 @@ describe('ReactComponentLifeCycle', () => {
926 }
927 }
928
871 - expect(() => {
872 - expect(() => ReactDOM.render(<WillReceiveProps />, container)).toErrorDev(
929 + await expect(async () => {
930 + await expect(async () => {
931 + await act(() => {
932 + root.render(<WillReceiveProps />);
933 + });
934 + }).toErrorDev(
935 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
936 'WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
937 ' componentWillReceiveProps\n\n' +
@@ -881,9 +943,7 @@ describe('ReactComponentLifeCycle', () => {
943 });
944 });
945
884 - it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => {
885 - const container = document.createElement('div');
886 -
946 + it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', async () => {
947 class AllLegacyLifecycles extends React.Component {
948 state = {};
949 getSnapshotBeforeUpdate() {}
@@ -896,10 +956,13 @@ describe('ReactComponentLifeCycle', () => {
956 }
957 }
958
899 - expect(() => {
900 - expect(() =>
901 - ReactDOM.render(<AllLegacyLifecycles />, container),
902 - ).toErrorDev(
959 + const root = ReactDOMClient.createRoot(document.createElement('div'));
960 + await expect(async () => {
961 + await expect(async () => {
962 + await act(() => {
963 + root.render(<AllLegacyLifecycles />);
964 + });
965 + }).toErrorDev(
966 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
967 'AllLegacyLifecycles uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
968 ' componentWillMount\n' +
@@ -926,7 +989,11 @@ describe('ReactComponentLifeCycle', () => {
989 }
990 }
991
929 - expect(() => ReactDOM.render(<WillMount />, container)).toErrorDev(
992 + await expect(async () => {
993 + await act(() => {
994 + root.render(<WillMount />);
995 + });
996 + }).toErrorDev(
997 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
998 'WillMount uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
999 ' UNSAFE_componentWillMount\n\n' +
@@ -945,10 +1012,12 @@ describe('ReactComponentLifeCycle', () => {
1012 }
1013 }
1014
948 - expect(() => {
949 - expect(() =>
950 - ReactDOM.render(<WillMountAndUpdate />, container),
951 - ).toErrorDev(
1015 + await expect(async () => {
1016 + await expect(async () => {
1017 + await act(() => {
1018 + root.render(<WillMountAndUpdate />);
1019 + });
1020 + }).toErrorDev(
1021 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
1022 'WillMountAndUpdate uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
1023 ' componentWillMount\n' +
@@ -970,8 +1039,13 @@ describe('ReactComponentLifeCycle', () => {
1039 }
1040 }
1041
973 - expect(() => {
974 - expect(() => ReactDOM.render(<WillReceiveProps />, container)).toErrorDev(
1042 + await expect(async () => {
1043 + await expect(
1044 + async () =>
1045 + await act(() => {
1046 + root.render(<WillReceiveProps />);
1047 + }),
1048 + ).toErrorDev(
1049 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
1050 'WillReceiveProps uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
1051 ' componentWillReceiveProps\n\n' +
@@ -984,7 +1058,7 @@ describe('ReactComponentLifeCycle', () => {
1058 });
1059
1060 if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) {
987 - it('calls effects on module-pattern component', function () {
1061 + it('calls effects on module-pattern component', async () => {
1062 const log = [];
1063
1064 function Parent() {
@@ -1019,17 +1093,21 @@ describe('ReactComponentLifeCycle', () => {
1093 x: PropTypes.number,
1094 };
1095
1022 - const div = document.createElement('div');
1023 - expect(() =>
1024 - ReactDOM.render(<Parent ref={c => c && log.push('ref')} />, div),
1025 - ).toErrorDev(
1096 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1097 + await expect(async () => {
1098 + await act(() => {
1099 + root.render(<Parent ref={c => c && log.push('ref')} />);
1100 + });
1101 + }).toErrorDev(
1102 'Warning: The <Parent /> component appears to be a function component that returns a class instance. ' +
1103 'Change Parent to a class that extends React.Component instead. ' +
1104 "If you can't use a class try assigning the prototype on the function as a workaround. " +
1105 '`Parent.prototype = React.Component.prototype`. ' +
1106 "Don't use an arrow function since it cannot be called with `new` by React.",
1107 );
1032 - ReactDOM.render(<Parent ref={c => c && log.push('ref')} />, div);
1108 + await act(() => {
1109 + root.render(<Parent ref={c => c && log.push('ref')} />);
1110 + });
1111
1112 expect(log).toEqual([
1113 'will mount',
@@ -1044,7 +1122,7 @@ describe('ReactComponentLifeCycle', () => {
1122 });
1123 }
1124
1047 - it('should warn if getDerivedStateFromProps returns undefined', () => {
1125 + it('should warn if getDerivedStateFromProps returns undefined', async () => {
1126 class MyComponent extends React.Component {
1127 state = {};
1128 static getDerivedStateFromProps() {}
@@ -1053,17 +1131,23 @@ describe('ReactComponentLifeCycle', () => {
1131 }
1132 }
1133
1056 - const div = document.createElement('div');
1057 - expect(() => ReactDOM.render(<MyComponent />, div)).toErrorDev(
1134 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1135 + await expect(async () => {
1136 + await act(() => {
1137 + root.render(<MyComponent />);
1138 + });
1139 + }).toErrorDev(
1140 'MyComponent.getDerivedStateFromProps(): A valid state object (or null) must ' +
1141 'be returned. You have returned undefined.',
1142 );
1143
1144 // De-duped
1063 - ReactDOM.render(<MyComponent />, div);
1145 + await act(() => {
1146 + root.render(<MyComponent />);
1147 + });
1148 });
1149
1066 - it('should warn if state is not initialized before getDerivedStateFromProps', () => {
1150 + it('should warn if state is not initialized before getDerivedStateFromProps', async () => {
1151 class MyComponent extends React.Component {
1152 static getDerivedStateFromProps() {
1153 return null;
@@ -1073,8 +1157,12 @@ describe('ReactComponentLifeCycle', () => {
1157 }
1158 }
1159
1076 - const div = document.createElement('div');
1077 - expect(() => ReactDOM.render(<MyComponent />, div)).toErrorDev(
1160 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1161 + await expect(async () => {
1162 + await act(() => {
1163 + root.render(<MyComponent />);
1164 + });
1165 + }).toErrorDev(
1166 '`MyComponent` uses `getDerivedStateFromProps` but its initial state is ' +
1167 'undefined. This is not recommended. Instead, define the initial state by ' +
1168 'assigning an object to `this.state` in the constructor of `MyComponent`. ' +
@@ -1082,10 +1170,12 @@ describe('ReactComponentLifeCycle', () => {
1170 );
1171
1172 // De-duped
1085 - ReactDOM.render(<MyComponent />, div);
1173 + await act(() => {
1174 + root.render(<MyComponent />);
1175 + });
1176 });
1177
1088 - it('should invoke both deprecated and new lifecycles if both are present', () => {
1178 + it('should invoke both deprecated and new lifecycles if both are present', async () => {
1179 const log = [];
1180
1181 class MyComponent extends React.Component {
@@ -1112,8 +1202,12 @@ describe('ReactComponentLifeCycle', () => {
1202 }
1203 }
1204
1115 - const div = document.createElement('div');
1116 - expect(() => ReactDOM.render(<MyComponent foo="bar" />, div)).toWarnDev(
1205 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1206 + await expect(async () => {
1207 + await act(() => {
1208 + root.render(<MyComponent foo="bar" />);
1209 + });
1210 + }).toWarnDev(
1211 [
1212 'componentWillMount has been renamed',
1213 'componentWillReceiveProps has been renamed',
@@ -1125,7 +1219,9 @@ describe('ReactComponentLifeCycle', () => {
1219
1220 log.length = 0;
1221
1128 - ReactDOM.render(<MyComponent foo="baz" />, div);
1222 + await act(() => {
1223 + root.render(<MyComponent foo="baz" />);
1224 + });
1225 expect(log).toEqual([
1226 'componentWillReceiveProps',
1227 'UNSAFE_componentWillReceiveProps',
@@ -1134,7 +1230,7 @@ describe('ReactComponentLifeCycle', () => {
1230 ]);
1231 });
1232
1137 - it('should not override state with stale values if prevState is spread within getDerivedStateFromProps', () => {
1233 + it('should not override state with stale values if prevState is spread within getDerivedStateFromProps', async () => {
1234 const divRef = React.createRef();
1235 let childInstance;
1236
@@ -1171,23 +1267,28 @@ describe('ReactComponentLifeCycle', () => {
1267
1268 const container = document.createElement('div');
1269 document.body.appendChild(container);
1174 - try {
1175 - ReactDOM.render(<Parent />, container);
1176 - expect(divRef.current.textContent).toBe('remote:0, local:0');
1270 + const root = ReactDOMClient.createRoot(container);
1271 +
1272 + await act(() => {
1273 + root.render(<Parent />);
1274 + });
1275 + expect(divRef.current.textContent).toBe('remote:0, local:0');
1276
1178 - // Trigger setState() calls
1277 + // Trigger setState() calls
1278 + await act(() => {
1279 childInstance.updateState();
1180 - expect(divRef.current.textContent).toBe('remote:1, local:1');
1280 + });
1281 + expect(divRef.current.textContent).toBe('remote:1, local:1');
1282
1182 - // Trigger batched setState() calls
1283 + // Trigger batched setState() calls
1284 + await act(() => {
1285 divRef.current.click();
1184 - expect(divRef.current.textContent).toBe('remote:2, local:2');
1185 - } finally {
1186 - document.body.removeChild(container);
1187 - }
1286 + });
1287 + expect(divRef.current.textContent).toBe('remote:2, local:2');
1288 + document.body.removeChild(container);
1289 });
1290
1190 - it('should pass the return value from getSnapshotBeforeUpdate to componentDidUpdate', () => {
1291 + it('should pass the return value from getSnapshotBeforeUpdate to componentDidUpdate', async () => {
1292 const log = [];
1293
1294 class MyComponent extends React.Component {
@@ -1216,22 +1317,24 @@ describe('ReactComponentLifeCycle', () => {
1317 }
1318 }
1319
1219 - const div = document.createElement('div');
1220 - ReactDOM.render(
1221 - <div>
1222 - <MyComponent value="foo" />
1223 - </div>,
1224 - div,
1225 - );
1320 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1321 + await act(() => {
1322 + root.render(
1323 + <div>
1324 + <MyComponent value="foo" />
1325 + </div>,
1326 + );
1327 + });
1328 expect(log).toEqual(['render']);
1329 log.length = 0;
1330
1229 - ReactDOM.render(
1230 - <div>
1231 - <MyComponent value="bar" />
1232 - </div>,
1233 - div,
1234 - );
1331 + await act(() => {
1332 + root.render(
1333 + <div>
1334 + <MyComponent value="bar" />
1335 + </div>,
1336 + );
1337 + });
1338 expect(log).toEqual([
1339 'render',
1340 'getSnapshotBeforeUpdate() prevProps:foo prevState:1',
@@ -1239,12 +1342,13 @@ describe('ReactComponentLifeCycle', () => {
1342 ]);
1343 log.length = 0;
1344
1242 - ReactDOM.render(
1243 - <div>
1244 - <MyComponent value="baz" />
1245 - </div>,
1246 - div,
1247 - );
1345 + await act(() => {
1346 + root.render(
1347 + <div>
1348 + <MyComponent value="baz" />
1349 + </div>,
1350 + );
1351 + });
1352 expect(log).toEqual([
1353 'render',
1354 'getSnapshotBeforeUpdate() prevProps:bar prevState:2',
@@ -1252,11 +1356,13 @@ describe('ReactComponentLifeCycle', () => {
1356 ]);
1357 log.length = 0;
1358
1255 - ReactDOM.render(<div />, div);
1359 + await act(() => {
1360 + root.render(<div />);
1361 + });
1362 expect(log).toEqual([]);
1363 });
1364
1259 - it('should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps', () => {
1365 + it('should pass previous state to shouldComponentUpdate even with getDerivedStateFromProps', async () => {
1366 const divRef = React.createRef();
1367 class SimpleComponent extends React.Component {
1368 constructor(props) {
@@ -1282,15 +1388,18 @@ describe('ReactComponentLifeCycle', () => {
1388 }
1389 }
1390
1285 - const div = document.createElement('div');
1286 -
1287 - ReactDOM.render(<SimpleComponent value="initial" />, div);
1391 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1392 + await act(() => {
1393 + root.render(<SimpleComponent value="initial" />);
1394 + });
1395 expect(divRef.current.textContent).toBe('value: initial');
1289 - ReactDOM.render(<SimpleComponent value="updated" />, div);
1396 + await act(() => {
1397 + root.render(<SimpleComponent value="updated" />);
1398 + });
1399 expect(divRef.current.textContent).toBe('value: updated');
1400 });
1401
1293 - it('should call getSnapshotBeforeUpdate before mutations are committed', () => {
1402 + it('should call getSnapshotBeforeUpdate before mutations are committed', async () => {
1403 const log = [];
1404
1405 class MyComponent extends React.Component {
@@ -1315,12 +1424,16 @@ describe('ReactComponentLifeCycle', () => {
1424 }
1425 }
1426
1318 - const div = document.createElement('div');
1319 - ReactDOM.render(<MyComponent value="foo" />, div);
1427 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1428 + await act(() => {
1429 + root.render(<MyComponent value="foo" />);
1430 + });
1431 expect(log).toEqual(['render']);
1432 log.length = 0;
1433
1323 - ReactDOM.render(<MyComponent value="bar" />, div);
1434 + await act(() => {
1435 + root.render(<MyComponent value="bar" />);
1436 + });
1437 expect(log).toEqual([
1438 'render',
1439 'getSnapshotBeforeUpdate',
@@ -1329,7 +1442,7 @@ describe('ReactComponentLifeCycle', () => {
1442 log.length = 0;
1443 });
1444
1332 - it('should warn if getSnapshotBeforeUpdate returns undefined', () => {
1445 + it('should warn if getSnapshotBeforeUpdate returns undefined', async () => {
1446 class MyComponent extends React.Component {
1447 getSnapshotBeforeUpdate() {}
1448 componentDidUpdate() {}
@@ -1338,18 +1451,27 @@ describe('ReactComponentLifeCycle', () => {
1451 }
1452 }
1453
1341 - const div = document.createElement('div');
1342 - ReactDOM.render(<MyComponent value="foo" />, div);
1343 - expect(() => ReactDOM.render(<MyComponent value="bar" />, div)).toErrorDev(
1454 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1455 + await act(() => {
1456 + root.render(<MyComponent value="foo" />);
1457 + });
1458 +
1459 + await expect(async () => {
1460 + await act(() => {
1461 + root.render(<MyComponent value="bar" />);
1462 + });
1463 + }).toErrorDev(
1464 'MyComponent.getSnapshotBeforeUpdate(): A snapshot value (or null) must ' +
1465 'be returned. You have returned undefined.',
1466 );
1467
1468 // De-duped
1349 - ReactDOM.render(<MyComponent value="baz" />, div);
1469 + await act(() => {
1470 + root.render(<MyComponent value="baz" />);
1471 + });
1472 });
1473
1352 - it('should warn if getSnapshotBeforeUpdate is defined with no componentDidUpdate', () => {
1474 + it('should warn if getSnapshotBeforeUpdate is defined with no componentDidUpdate', async () => {
1475 class MyComponent extends React.Component {
1476 getSnapshotBeforeUpdate() {
1477 return null;
@@ -1359,17 +1481,23 @@ describe('ReactComponentLifeCycle', () => {
1481 }
1482 }
1483
1362 - const div = document.createElement('div');
1363 - expect(() => ReactDOM.render(<MyComponent />, div)).toErrorDev(
1484 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1485 + await expect(async () => {
1486 + await act(() => {
1487 + root.render(<MyComponent />);
1488 + });
1489 + }).toErrorDev(
1490 'MyComponent: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' +
1491 'This component defines getSnapshotBeforeUpdate() only.',
1492 );
1493
1494 // De-duped
1369 - ReactDOM.render(<MyComponent />, div);
1495 + await act(() => {
1496 + root.render(<MyComponent />);
1497 + });
1498 });
1499
1372 - it('warns about deprecated unsafe lifecycles', function () {
1500 + it('warns about deprecated unsafe lifecycles', async () => {
1501 class MyComponent extends React.Component {
1502 componentWillMount() {}
1503 componentWillReceiveProps() {}
@@ -1379,8 +1507,13 @@ describe('ReactComponentLifeCycle', () => {
1507 }
1508 }
1509
1382 - const container = document.createElement('div');
1383 - expect(() => ReactDOM.render(<MyComponent x={1} />, container)).toWarnDev(
1510 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1511 +
1512 + await expect(async () => {
1513 + await act(() => {
1514 + root.render(<MyComponent x={1} />);
1515 + });
1516 + }).toWarnDev(
1517 [
1518 /* eslint-disable max-len */
1519 `Warning: componentWillMount has been renamed, and is not recommended for use. See https://reactjs.org/link/unsafe-component-lifecycles for details.
@@ -1408,14 +1541,18 @@ Please update the following components: MyComponent`,
1541 );
1542
1543 // Dedupe check (update and instantiate new)
1411 - ReactDOM.render(<MyComponent x={2} />, container);
1412 - ReactDOM.render(<MyComponent key="new" x={1} />, container);
1544 + await act(() => {
1545 + root.render(<MyComponent x={2} />);
1546 + });
1547 + await act(() => {
1548 + root.render(<MyComponent key="new" x={1} />);
1549 + });
1550 });
1551
1552 describe('react-lifecycles-compat', () => {
1553 const {polyfill} = require('react-lifecycles-compat');
1554
1418 - it('should not warn for components with polyfilled getDerivedStateFromProps', () => {
1555 + it('should not warn for components with polyfilled getDerivedStateFromProps', async () => {
1556 class PolyfilledComponent extends React.Component {
1557 state = {};
1558 static getDerivedStateFromProps() {
@@ -1428,16 +1565,17 @@ Please update the following components: MyComponent`,
1565
1566 polyfill(PolyfilledComponent);
1567
1431 - const container = document.createElement('div');
1432 - ReactDOM.render(
1433 - <React.StrictMode>
1434 - <PolyfilledComponent />
1435 - </React.StrictMode>,
1436 - container,
1437 - );
1568 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1569 + await act(() => {
1570 + root.render(
1571 + <React.StrictMode>
1572 + <PolyfilledComponent />
1573 + </React.StrictMode>,
1574 + );
1575 + });
1576 });
1577
1440 - it('should not warn for components with polyfilled getSnapshotBeforeUpdate', () => {
1578 + it('should not warn for components with polyfilled getSnapshotBeforeUpdate', async () => {
1579 class PolyfilledComponent extends React.Component {
1580 getSnapshotBeforeUpdate() {
1581 return null;
@@ -1450,13 +1588,14 @@ Please update the following components: MyComponent`,
1588
1589 polyfill(PolyfilledComponent);
1590
1453 - const container = document.createElement('div');
1454 - ReactDOM.render(
1455 - <React.StrictMode>
1456 - <PolyfilledComponent />
1457 - </React.StrictMode>,
1458 - container,
1459 - );
1591 + const root = ReactDOMClient.createRoot(document.createElement('div'));
1592 + await act(() => {
1593 + root.render(
1594 + <React.StrictMode>
1595 + <PolyfilledComponent />
1596 + </React.StrictMode>,
1597 + );
1598 + });
1599 });
1600 });
1601 });