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
});