New commit phase lifecycle: getSnapshotBeforeUpdate (#12404)
* Implemented new getSnapshotBeforeUpdate lifecycle * Store snapshot value from Fiber to instance (__reactInternalSnapshotBeforeUpdate) * Use commitAllHostEffects() traversal for getSnapshotBeforeUpdate() * Added DEV warnings and tests for new lifecycle * Don't invoke legacy lifecycles if getSnapshotBeforeUpdate() is defined. DEV warn about this. * Converted did-warn objects to Sets in ReactFiberClassComponent * Replaced redundant new lifecycle checks in a few methods * Check for polyfill suppress flag on cWU as well before warning * Added Snapshot bit to HostEffectMask
Brian Vaughn committed
Mar 26, 2018 at 13:28 UTC
e1a106a071dff17737a7afb49a0b71899d60c248
12 files changed
+597
-149
packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js
+235
-7
@@ -591,6 +591,7 @@ describe('ReactComponentLifeCycle', () => {
591
}
592
componentDidMount = logger('outer componentDidMount');
593
shouldComponentUpdate = logger('outer shouldComponentUpdate');
594
+ getSnapshotBeforeUpdate = logger('outer getSnapshotBeforeUpdate');
595
componentDidUpdate = logger('outer componentDidUpdate');
596
componentWillUnmount = logger('outer componentWillUnmount');
597
render() {
@@ -610,6 +611,7 @@ describe('ReactComponentLifeCycle', () => {
611
}
612
componentDidMount = logger('inner componentDidMount');
613
shouldComponentUpdate = logger('inner shouldComponentUpdate');
614
+ getSnapshotBeforeUpdate = logger('inner getSnapshotBeforeUpdate');
615
componentDidUpdate = logger('inner componentDidUpdate');
616
componentWillUnmount = logger('inner componentWillUnmount');
617
render() {
@@ -635,6 +637,8 @@ describe('ReactComponentLifeCycle', () => {
637
'outer shouldComponentUpdate',
638
'inner getDerivedStateFromProps',
639
'inner shouldComponentUpdate',
640
+ 'inner getSnapshotBeforeUpdate',
641
+ 'outer getSnapshotBeforeUpdate',
642
'inner componentDidUpdate',
643
'outer componentDidUpdate',
644
]);
@@ -669,10 +673,38 @@ describe('ReactComponentLifeCycle', () => {
673
674
const container = document.createElement('div');
675
expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
672
- 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.',
676
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.',
677
);
678
});
679
680
+ it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => {
681
+ class Component extends React.Component {
682
+ state = {};
683
+ getSnapshotBeforeUpdate() {
684
+ return null;
685
+ }
686
+ componentWillMount() {
687
+ throw Error('unexpected');
688
+ }
689
+ componentWillReceiveProps() {
690
+ throw Error('unexpected');
691
+ }
692
+ componentWillUpdate() {
693
+ throw Error('unexpected');
694
+ }
695
+ componentDidUpdate() {}
696
+ render() {
697
+ return null;
698
+ }
699
+ }
700
+
701
+ const container = document.createElement('div');
702
+ expect(() => ReactDOM.render(<Component value={1} />, container)).toWarnDev(
703
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.',
704
+ );
705
+ ReactDOM.render(<Component value={2} />, container);
706
+ });
707
+
708
it('should not invoke new unsafe lifecycles (cWM/cWRP/cWU) if static gDSFP is present', () => {
709
class Component extends React.Component {
710
state = {};
@@ -694,9 +726,10 @@ describe('ReactComponentLifeCycle', () => {
726
}
727
728
const container = document.createElement('div');
697
- expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
698
- 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.',
729
+ expect(() => ReactDOM.render(<Component value={1} />, container)).toWarnDev(
730
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.',
731
);
732
+ ReactDOM.render(<Component value={2} />, container);
733
});
734
735
it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
@@ -716,7 +749,7 @@ describe('ReactComponentLifeCycle', () => {
749
}
750
751
expect(() => ReactDOM.render(<AllLegacyLifecycles />, container)).toWarnDev(
719
- 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
752
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
753
'AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
754
' componentWillMount\n' +
755
' UNSAFE_componentWillReceiveProps\n' +
@@ -737,7 +770,7 @@ describe('ReactComponentLifeCycle', () => {
770
}
771
772
expect(() => ReactDOM.render(<WillMount />, container)).toWarnDev(
740
- 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
773
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
774
'WillMount uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
775
' UNSAFE_componentWillMount\n\n' +
776
'The above lifecycles should be removed. Learn more about this warning here:\n' +
@@ -757,7 +790,7 @@ describe('ReactComponentLifeCycle', () => {
790
}
791
792
expect(() => ReactDOM.render(<WillMountAndUpdate />, container)).toWarnDev(
760
- 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
793
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
794
'WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
795
' componentWillMount\n' +
796
' UNSAFE_componentWillUpdate\n\n' +
@@ -777,7 +810,7 @@ describe('ReactComponentLifeCycle', () => {
810
}
811
812
expect(() => ReactDOM.render(<WillReceiveProps />, container)).toWarnDev(
780
- 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
813
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
814
'WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
815
' componentWillReceiveProps\n\n' +
816
'The above lifecycles should be removed. Learn more about this warning here:\n' +
@@ -785,6 +818,88 @@ describe('ReactComponentLifeCycle', () => {
818
);
819
});
820
821
+ it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => {
822
+ const container = document.createElement('div');
823
+
824
+ class AllLegacyLifecycles extends React.Component {
825
+ state = {};
826
+ getSnapshotBeforeUpdate() {}
827
+ componentWillMount() {}
828
+ UNSAFE_componentWillReceiveProps() {}
829
+ componentWillUpdate() {}
830
+ componentDidUpdate() {}
831
+ render() {
832
+ return null;
833
+ }
834
+ }
835
+
836
+ expect(() => ReactDOM.render(<AllLegacyLifecycles />, container)).toWarnDev(
837
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
838
+ 'AllLegacyLifecycles uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
839
+ ' componentWillMount\n' +
840
+ ' UNSAFE_componentWillReceiveProps\n' +
841
+ ' componentWillUpdate\n\n' +
842
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
843
+ 'https://fb.me/react-async-component-lifecycle-hooks',
844
+ );
845
+
846
+ class WillMount extends React.Component {
847
+ state = {};
848
+ getSnapshotBeforeUpdate() {}
849
+ UNSAFE_componentWillMount() {}
850
+ componentDidUpdate() {}
851
+ render() {
852
+ return null;
853
+ }
854
+ }
855
+
856
+ expect(() => ReactDOM.render(<WillMount />, container)).toWarnDev(
857
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
858
+ 'WillMount uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
859
+ ' UNSAFE_componentWillMount\n\n' +
860
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
861
+ 'https://fb.me/react-async-component-lifecycle-hooks',
862
+ );
863
+
864
+ class WillMountAndUpdate extends React.Component {
865
+ state = {};
866
+ getSnapshotBeforeUpdate() {}
867
+ componentWillMount() {}
868
+ UNSAFE_componentWillUpdate() {}
869
+ componentDidUpdate() {}
870
+ render() {
871
+ return null;
872
+ }
873
+ }
874
+
875
+ expect(() => ReactDOM.render(<WillMountAndUpdate />, container)).toWarnDev(
876
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
877
+ 'WillMountAndUpdate uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
878
+ ' componentWillMount\n' +
879
+ ' UNSAFE_componentWillUpdate\n\n' +
880
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
881
+ 'https://fb.me/react-async-component-lifecycle-hooks',
882
+ );
883
+
884
+ class WillReceiveProps extends React.Component {
885
+ state = {};
886
+ getSnapshotBeforeUpdate() {}
887
+ componentWillReceiveProps() {}
888
+ componentDidUpdate() {}
889
+ render() {
890
+ return null;
891
+ }
892
+ }
893
+
894
+ expect(() => ReactDOM.render(<WillReceiveProps />, container)).toWarnDev(
895
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
896
+ 'WillReceiveProps uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
897
+ ' componentWillReceiveProps\n\n' +
898
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
899
+ 'https://fb.me/react-async-component-lifecycle-hooks',
900
+ );
901
+ });
902
+
903
it('calls effects on module-pattern component', function() {
904
const log = [];
905
@@ -961,4 +1076,117 @@ describe('ReactComponentLifeCycle', () => {
1076
ReactTestUtils.Simulate.click(divRef.current);
1077
expect(divRef.current.textContent).toBe('remote:2, local:2');
1078
});
1079
+
1080
+ it('should pass the return value from getSnapshotBeforeUpdate to componentDidUpdate', () => {
1081
+ const log = [];
1082
+
1083
+ class MyComponent extends React.Component {
1084
+ state = {
1085
+ value: 0,
1086
+ };
1087
+ static getDerivedStateFromProps(nextProps, prevState) {
1088
+ return {
1089
+ value: prevState.value + 1,
1090
+ };
1091
+ }
1092
+ getSnapshotBeforeUpdate(prevProps, prevState) {
1093
+ log.push(
1094
+ `getSnapshotBeforeUpdate() prevProps:${prevProps.value} prevState:${
1095
+ prevState.value
1096
+ }`,
1097
+ );
1098
+ return 'abc';
1099
+ }
1100
+ componentDidUpdate(prevProps, prevState, snapshot) {
1101
+ log.push(
1102
+ `componentDidUpdate() prevProps:${prevProps.value} prevState:${
1103
+ prevState.value
1104
+ } snapshot:${snapshot}`,
1105
+ );
1106
+ }
1107
+ render() {
1108
+ log.push('render');
1109
+ return null;
1110
+ }
1111
+ }
1112
+
1113
+ const div = document.createElement('div');
1114
+ ReactDOM.render(
1115
+ <div>
1116
+ <MyComponent value="foo" />
1117
+ </div>,
1118
+ div,
1119
+ );
1120
+ expect(log).toEqual(['render']);
1121
+ log.length = 0;
1122
+
1123
+ ReactDOM.render(
1124
+ <div>
1125
+ <MyComponent value="bar" />
1126
+ </div>,
1127
+ div,
1128
+ );
1129
+ expect(log).toEqual([
1130
+ 'render',
1131
+ 'getSnapshotBeforeUpdate() prevProps:foo prevState:1',
1132
+ 'componentDidUpdate() prevProps:foo prevState:1 snapshot:abc',
1133
+ ]);
1134
+ log.length = 0;
1135
+
1136
+ ReactDOM.render(
1137
+ <div>
1138
+ <MyComponent value="baz" />
1139
+ </div>,
1140
+ div,
1141
+ );
1142
+ expect(log).toEqual([
1143
+ 'render',
1144
+ 'getSnapshotBeforeUpdate() prevProps:bar prevState:2',
1145
+ 'componentDidUpdate() prevProps:bar prevState:2 snapshot:abc',
1146
+ ]);
1147
+ log.length = 0;
1148
+
1149
+ ReactDOM.render(<div />, div);
1150
+ expect(log).toEqual([]);
1151
+ });
1152
+
1153
+ it('should warn if getSnapshotBeforeUpdate returns undefined', () => {
1154
+ class MyComponent extends React.Component {
1155
+ getSnapshotBeforeUpdate() {}
1156
+ componentDidUpdate() {}
1157
+ render() {
1158
+ return null;
1159
+ }
1160
+ }
1161
+
1162
+ const div = document.createElement('div');
1163
+ ReactDOM.render(<MyComponent value="foo" />, div);
1164
+ expect(() => ReactDOM.render(<MyComponent value="bar" />, div)).toWarnDev(
1165
+ 'MyComponent.getSnapshotBeforeUpdate(): A snapshot value (or null) must ' +
1166
+ 'be returned. You have returned undefined.',
1167
+ );
1168
+
1169
+ // De-duped
1170
+ ReactDOM.render(<MyComponent value="baz" />, div);
1171
+ });
1172
+
1173
+ it('should warn if getSnapshotBeforeUpdate is defined with no componentDidUpdate', () => {
1174
+ class MyComponent extends React.Component {
1175
+ getSnapshotBeforeUpdate() {
1176
+ return null;
1177
+ }
1178
+ render() {
1179
+ return null;
1180
+ }
1181
+ }
1182
+
1183
+ const div = document.createElement('div');
1184
+ expect(() => ReactDOM.render(<MyComponent />, div)).toWarnDev(
1185
+ 'MyComponent: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' +
1186
+ 'This component defines getSnapshotBeforeUpdate() only.',
1187
+ );
1188
+
1189
+ // De-duped
1190
+ ReactDOM.render(<MyComponent />, div);
1191
+ });
1192
});
packages/react-dom/src/__tests__/ReactErrorBoundaries-test.internal.js
+40
@@ -2081,4 +2081,44 @@ describe('ReactErrorBoundaries', () => {
2081
});
2082
}).toThrow('foo error');
2083
});
2084
+
2085
+ it('handles errors that occur in before-mutation commit hook', () => {
2086
+ const errors = [];
2087
+ let caughtError;
2088
+ class Parent extends React.Component {
2089
+ getSnapshotBeforeUpdate() {
2090
+ errors.push('parent sad');
2091
+ throw new Error('parent sad');
2092
+ }
2093
+ componentDidUpdate() {}
2094
+ render() {
2095
+ return <Child {...this.props} />;
2096
+ }
2097
+ }
2098
+ class Child extends React.Component {
2099
+ getSnapshotBeforeUpdate() {
2100
+ errors.push('child sad');
2101
+ throw new Error('child sad');
2102
+ }
2103
+ componentDidUpdate() {}
2104
+ render() {
2105
+ return <div />;
2106
+ }
2107
+ }
2108
+
2109
+ const container = document.createElement('div');
2110
+ ReactDOM.render(<Parent value={1} />, container);
2111
+ try {
2112
+ ReactDOM.render(<Parent value={2} />, container);
2113
+ } catch (e) {
2114
+ if (e.message !== 'parent sad' && e.message !== 'child sad') {
2115
+ throw e;
2116
+ }
2117
+ caughtError = e;
2118
+ }
2119
+
2120
+ expect(errors).toEqual(['child sad', 'parent sad']);
2121
+ // Error should be the first thrown
2122
+ expect(caughtError.message).toBe('child sad');
2123
+ });
2124
});
packages/react-reconciler/src/ReactDebugFiberPerf.js
+2
-1
@@ -31,7 +31,8 @@ type MeasurementPhase =
31
| 'componentWillUpdate'
32
| 'componentDidUpdate'
33
| 'componentDidMount'
34
- | 'getChildContext';
34
+ | 'getChildContext'
35
+ | 'getSnapshotBeforeUpdate';
36
37
// Prefix measurements so that it's possible to filter them.
38
// Longer prefixes are hard to read in DevTools.
packages/react-reconciler/src/ReactFiberClassComponent.js
+109
-50
@@ -12,7 +12,7 @@ import type {ExpirationTime} from './ReactFiberExpirationTime';
12
import type {LegacyContext} from './ReactFiberContext';
13
import type {CapturedValue} from './ReactCapturedValue';
14
15
-import {Update} from 'shared/ReactTypeOfSideEffect';
15
+import {Update, Snapshot} from 'shared/ReactTypeOfSideEffect';
16
import {
17
enableGetDerivedStateFromCatch,
18
debugRenderPhaseSideEffects,
@@ -41,23 +41,26 @@ const isArray = Array.isArray;
41
let didWarnAboutStateAssignmentForComponent;
42
let didWarnAboutUndefinedDerivedState;
43
let didWarnAboutUninitializedState;
44
+let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
45
let didWarnAboutLegacyLifecyclesAndDerivedState;
46
let warnOnInvalidCallback;
47
48
if (__DEV__) {
48
- didWarnAboutStateAssignmentForComponent = {};
49
- didWarnAboutUndefinedDerivedState = {};
50
- didWarnAboutUninitializedState = {};
51
- didWarnAboutLegacyLifecyclesAndDerivedState = {};
49
+ didWarnAboutStateAssignmentForComponent = new Set();
50
+ didWarnAboutUndefinedDerivedState = new Set();
51
+ didWarnAboutUninitializedState = new Set();
52
+ didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set();
53
+ didWarnAboutLegacyLifecyclesAndDerivedState = new Set();
54
53
- const didWarnOnInvalidCallback = {};
55
+ const didWarnOnInvalidCallback = new Set();
56
57
warnOnInvalidCallback = function(callback: mixed, callerName: string) {
58
if (callback === null || typeof callback === 'function') {
59
return;
60
}
61
const key = `${callerName}_${(callback: any)}`;
60
- if (!didWarnOnInvalidCallback[key]) {
62
+ if (!didWarnOnInvalidCallback.has(key)) {
63
+ didWarnOnInvalidCallback.add(key);
64
warning(
65
false,
66
'%s(...): Expected the last optional `callback` argument to be a ' +
@@ -65,7 +68,6 @@ if (__DEV__) {
68
callerName,
69
callback,
70
);
68
- didWarnOnInvalidCallback[key] = true;
71
}
72
};
73
@@ -364,6 +366,22 @@ export default function(
366
name,
367
name,
368
);
369
+
370
+ if (
371
+ typeof instance.getSnapshotBeforeUpdate === 'function' &&
372
+ typeof instance.componentDidUpdate !== 'function' &&
373
+ typeof instance.componentDidUpdate !== 'function' &&
374
+ !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(type)
375
+ ) {
376
+ didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(type);
377
+ warning(
378
+ false,
379
+ '%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' +
380
+ 'This component defines getSnapshotBeforeUpdate() only.',
381
+ getComponentName(workInProgress),
382
+ );
383
+ }
384
+
385
const noInstanceGetDerivedStateFromProps =
386
typeof instance.getDerivedStateFromProps !== 'function';
387
warning(
@@ -435,24 +453,30 @@ export default function(
453
adoptClassInstance(workInProgress, instance);
454
455
if (__DEV__) {
438
- if (typeof ctor.getDerivedStateFromProps === 'function') {
439
- if (state === null) {
440
- const componentName = getComponentName(workInProgress) || 'Component';
441
- if (!didWarnAboutUninitializedState[componentName]) {
442
- warning(
443
- false,
444
- '%s: Did not properly initialize state during construction. ' +
445
- 'Expected state to be an object, but it was %s.',
446
- componentName,
447
- instance.state === null ? 'null' : 'undefined',
448
- );
449
- didWarnAboutUninitializedState[componentName] = true;
450
- }
456
+ if (
457
+ typeof ctor.getDerivedStateFromProps === 'function' &&
458
+ state === null
459
+ ) {
460
+ const componentName = getComponentName(workInProgress) || 'Component';
461
+ if (!didWarnAboutUninitializedState.has(componentName)) {
462
+ didWarnAboutUninitializedState.add(componentName);
463
+ warning(
464
+ false,
465
+ '%s: Did not properly initialize state during construction. ' +
466
+ 'Expected state to be an object, but it was %s.',
467
+ componentName,
468
+ instance.state === null ? 'null' : 'undefined',
469
+ );
470
}
471
+ }
472
453
- // If getDerivedStateFromProps() is defined, "unsafe" lifecycles won't be called.
454
- // Warn about these lifecycles if they are present.
455
- // Don't warn about react-lifecycles-compat polyfilled methods though.
473
+ // If new component APIs are defined, "unsafe" lifecycles won't be called.
474
+ // Warn about these lifecycles if they are present.
475
+ // Don't warn about react-lifecycles-compat polyfilled methods though.
476
+ if (
477
+ typeof ctor.getDerivedStateFromProps === 'function' ||
478
+ typeof instance.getSnapshotBeforeUpdate === 'function'
479
+ ) {
480
let foundWillMountName = null;
481
let foundWillReceivePropsName = null;
482
let foundWillUpdateName = null;
@@ -475,7 +499,10 @@ export default function(
499
) {
500
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
501
}
478
- if (typeof instance.componentWillUpdate === 'function') {
502
+ if (
503
+ typeof instance.componentWillUpdate === 'function' &&
504
+ instance.componentWillUpdate.__suppressDeprecationWarning !== true
505
+ ) {
506
foundWillUpdateName = 'componentWillUpdate';
507
} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
508
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
@@ -486,23 +513,26 @@ export default function(
513
foundWillUpdateName !== null
514
) {
515
const componentName = getComponentName(workInProgress) || 'Component';
489
- if (!didWarnAboutLegacyLifecyclesAndDerivedState[componentName]) {
516
+ const newApiName =
517
+ typeof ctor.getDerivedStateFromProps === 'function'
518
+ ? 'getDerivedStateFromProps()'
519
+ : 'getSnapshotBeforeUpdate()';
520
+ if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(componentName)) {
521
+ didWarnAboutLegacyLifecyclesAndDerivedState.add(componentName);
522
warning(
523
false,
492
- 'Unsafe legacy lifecycles will not be called for components using ' +
493
- 'the new getDerivedStateFromProps() API.\n\n' +
494
- '%s uses getDerivedStateFromProps() but also contains the following legacy lifecycles:' +
495
- '%s%s%s\n\n' +
524
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
525
+ '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' +
526
'The above lifecycles should be removed. Learn more about this warning here:\n' +
527
'https://fb.me/react-async-component-lifecycle-hooks',
528
componentName,
529
+ newApiName,
530
foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
531
foundWillReceivePropsName !== null
532
? `\n ${foundWillReceivePropsName}`
533
: '',
534
foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
535
);
505
- didWarnAboutLegacyLifecyclesAndDerivedState[componentName] = true;
536
}
537
}
538
}
@@ -583,7 +613,8 @@ export default function(
613
if (instance.state !== oldState) {
614
if (__DEV__) {
615
const componentName = getComponentName(workInProgress) || 'Component';
586
- if (!didWarnAboutStateAssignmentForComponent[componentName]) {
616
+ if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
617
+ didWarnAboutStateAssignmentForComponent.add(componentName);
618
warning(
619
false,
620
'%s.componentWillReceiveProps(): Assigning directly to ' +
@@ -591,7 +622,6 @@ export default function(
622
'constructor). Use setState instead.',
623
componentName,
624
);
594
- didWarnAboutStateAssignmentForComponent[componentName] = true;
625
}
626
}
627
updater.enqueueReplaceState(instance, instance.state, null);
@@ -625,14 +655,14 @@ export default function(
655
if (__DEV__) {
656
if (partialState === undefined) {
657
const componentName = getComponentName(workInProgress) || 'Component';
628
- if (!didWarnAboutUndefinedDerivedState[componentName]) {
658
+ if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
659
+ didWarnAboutUndefinedDerivedState.add(componentName);
660
warning(
661
false,
662
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
663
'You have returned undefined.',
664
componentName,
665
);
635
- didWarnAboutUndefinedDerivedState[componentName] = componentName;
666
}
667
}
668
}
@@ -679,11 +709,12 @@ export default function(
709
}
710
711
// In order to support react-lifecycles-compat polyfilled components,
682
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
712
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
713
if (
714
+ typeof ctor.getDerivedStateFromProps !== 'function' &&
715
+ typeof instance.getSnapshotBeforeUpdate !== 'function' &&
716
(typeof instance.UNSAFE_componentWillMount === 'function' ||
685
- typeof instance.componentWillMount === 'function') &&
686
- typeof ctor.getDerivedStateFromProps !== 'function'
717
+ typeof instance.componentWillMount === 'function')
718
) {
719
callComponentWillMount(workInProgress, instance);
720
// If we had additional state updates during this life-cycle, let's
@@ -719,16 +750,20 @@ export default function(
750
const newUnmaskedContext = getUnmaskedContext(workInProgress);
751
const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
752
753
+ const hasNewLifecycles =
754
+ typeof ctor.getDerivedStateFromProps === 'function' ||
755
+ typeof instance.getSnapshotBeforeUpdate === 'function';
756
+
757
// Note: During these life-cycles, instance.props/instance.state are what
758
// ever the previously attempted to render - not the "current". However,
759
// during componentDidUpdate we pass the "current" props.
760
761
// In order to support react-lifecycles-compat polyfilled components,
727
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
762
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
763
if (
764
+ !hasNewLifecycles &&
765
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
730
- typeof instance.componentWillReceiveProps === 'function') &&
731
- typeof ctor.getDerivedStateFromProps !== 'function'
766
+ typeof instance.componentWillReceiveProps === 'function')
767
) {
768
if (oldProps !== newProps || oldContext !== newContext) {
769
callComponentWillReceiveProps(
@@ -835,11 +870,11 @@ export default function(
870
871
if (shouldUpdate) {
872
// In order to support react-lifecycles-compat polyfilled components,
838
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
873
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
874
if (
875
+ !hasNewLifecycles &&
876
(typeof instance.UNSAFE_componentWillMount === 'function' ||
841
- typeof instance.componentWillMount === 'function') &&
842
- typeof ctor.getDerivedStateFromProps !== 'function'
877
+ typeof instance.componentWillMount === 'function')
878
) {
879
startPhaseTimer(workInProgress, 'componentWillMount');
880
if (typeof instance.componentWillMount === 'function') {
@@ -891,16 +926,20 @@ export default function(
926
const newUnmaskedContext = getUnmaskedContext(workInProgress);
927
const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
928
929
+ const hasNewLifecycles =
930
+ typeof ctor.getDerivedStateFromProps === 'function' ||
931
+ typeof instance.getSnapshotBeforeUpdate === 'function';
932
+
933
// Note: During these life-cycles, instance.props/instance.state are what
934
// ever the previously attempted to render - not the "current". However,
935
// during componentDidUpdate we pass the "current" props.
936
937
// In order to support react-lifecycles-compat polyfilled components,
899
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
938
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
939
if (
940
+ !hasNewLifecycles &&
941
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
902
- typeof instance.componentWillReceiveProps === 'function') &&
903
- typeof ctor.getDerivedStateFromProps !== 'function'
942
+ typeof instance.componentWillReceiveProps === 'function')
943
) {
944
if (oldProps !== newProps || oldContext !== newContext) {
945
callComponentWillReceiveProps(
@@ -917,6 +956,7 @@ export default function(
956
// TODO: Previous state can be null.
957
let newState;
958
let derivedStateFromCatch;
959
+
960
if (workInProgress.updateQueue !== null) {
961
newState = processUpdateQueue(
962
current,
@@ -998,6 +1038,14 @@ export default function(
1038
workInProgress.effectTag |= Update;
1039
}
1040
}
1041
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
1042
+ if (
1043
+ oldProps !== current.memoizedProps ||
1044
+ oldState !== current.memoizedState
1045
+ ) {
1046
+ workInProgress.effectTag |= Snapshot;
1047
+ }
1048
+ }
1049
return false;
1050
}
1051
@@ -1012,11 +1060,11 @@ export default function(
1060
1061
if (shouldUpdate) {
1062
// In order to support react-lifecycles-compat polyfilled components,
1015
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
1063
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
1064
if (
1065
+ !hasNewLifecycles &&
1066
(typeof instance.UNSAFE_componentWillUpdate === 'function' ||
1018
- typeof instance.componentWillUpdate === 'function') &&
1019
- typeof ctor.getDerivedStateFromProps !== 'function'
1067
+ typeof instance.componentWillUpdate === 'function')
1068
) {
1069
startPhaseTimer(workInProgress, 'componentWillUpdate');
1070
if (typeof instance.componentWillUpdate === 'function') {
@@ -1030,6 +1078,9 @@ export default function(
1078
if (typeof instance.componentDidUpdate === 'function') {
1079
workInProgress.effectTag |= Update;
1080
}
1081
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
1082
+ workInProgress.effectTag |= Snapshot;
1083
+ }
1084
} else {
1085
// If an update was already in progress, we should schedule an Update
1086
// effect even though we're bailing out, so that cWU/cDU are called.
@@ -1041,6 +1092,14 @@ export default function(
1092
workInProgress.effectTag |= Update;
1093
}
1094
}
1095
+ if (typeof instance.getSnapshotBeforeUpdate === 'function') {
1096
+ if (
1097
+ oldProps !== current.memoizedProps ||
1098
+ oldState !== current.memoizedState
1099
+ ) {
1100
+ workInProgress.effectTag |= Snapshot;
1101
+ }
1102
+ }
1103
1104
// If shouldComponentUpdate returned false, we should still update the
1105
// memoized props/state to indicate that this work can be reused.
packages/react-reconciler/src/ReactFiberCommitWork.js
+75
-2
@@ -27,7 +27,12 @@ import {
27
CallComponent,
28
} from 'shared/ReactTypeOfWork';
29
import ReactErrorUtils from 'shared/ReactErrorUtils';
30
-import {Placement, Update, ContentReset} from 'shared/ReactTypeOfSideEffect';
30
+import {
31
+ Placement,
32
+ Update,
33
+ ContentReset,
34
+ Snapshot,
35
+} from 'shared/ReactTypeOfSideEffect';
36
import invariant from 'fbjs/lib/invariant';
37
import warning from 'fbjs/lib/warning';
38
@@ -44,6 +49,11 @@ const {
49
clearCaughtError,
50
} = ReactErrorUtils;
51
52
+let didWarnAboutUndefinedSnapshotBeforeUpdate: Set<mixed> | null = null;
53
+if (__DEV__) {
54
+ didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
55
+}
56
+
57
function logError(boundary: Fiber, errorInfo: CapturedValue<mixed>) {
58
const source = errorInfo.source;
59
let stack = errorInfo.stack;
@@ -151,6 +161,63 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
161
}
162
}
163
164
+ function commitBeforeMutationLifeCycles(
165
+ current: Fiber | null,
166
+ finishedWork: Fiber,
167
+ ): void {
168
+ switch (finishedWork.tag) {
169
+ case ClassComponent: {
170
+ if (finishedWork.effectTag & Snapshot) {
171
+ if (current !== null) {
172
+ const prevProps = current.memoizedProps;
173
+ const prevState = current.memoizedState;
174
+ startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
175
+ const instance = finishedWork.stateNode;
176
+ instance.props = finishedWork.memoizedProps;
177
+ instance.state = finishedWork.memoizedState;
178
+ const snapshot = instance.getSnapshotBeforeUpdate(
179
+ prevProps,
180
+ prevState,
181
+ );
182
+ if (__DEV__) {
183
+ const didWarnSet = ((didWarnAboutUndefinedSnapshotBeforeUpdate: any): Set<
184
+ mixed,
185
+ >);
186
+ if (
187
+ snapshot === undefined &&
188
+ !didWarnSet.has(finishedWork.type)
189
+ ) {
190
+ didWarnSet.add(finishedWork.type);
191
+ warning(
192
+ false,
193
+ '%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' +
194
+ 'must be returned. You have returned undefined.',
195
+ getComponentName(finishedWork),
196
+ );
197
+ }
198
+ }
199
+ instance.__reactInternalSnapshotBeforeUpdate = snapshot;
200
+ stopPhaseTimer();
201
+ }
202
+ }
203
+ return;
204
+ }
205
+ case HostRoot:
206
+ case HostComponent:
207
+ case HostText:
208
+ case HostPortal:
209
+ // Nothing to do for these component types
210
+ return;
211
+ default: {
212
+ invariant(
213
+ false,
214
+ 'This unit of work tag should not have side-effects. This error is ' +
215
+ 'likely caused by a bug in React. Please file an issue.',
216
+ );
217
+ }
218
+ }
219
+ }
220
+
221
function commitLifeCycles(
222
finishedRoot: FiberRoot,
223
current: Fiber | null,
@@ -174,7 +241,11 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
241
startPhaseTimer(finishedWork, 'componentDidUpdate');
242
instance.props = finishedWork.memoizedProps;
243
instance.state = finishedWork.memoizedState;
177
- instance.componentDidUpdate(prevProps, prevState);
244
+ instance.componentDidUpdate(
245
+ prevProps,
246
+ prevState,
247
+ instance.__reactInternalSnapshotBeforeUpdate,
248
+ );
249
stopPhaseTimer();
250
}
251
}
@@ -492,6 +563,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
563
commitContainer(finishedWork);
564
},
565
commitLifeCycles,
566
+ commitBeforeMutationLifeCycles,
567
commitErrorLogging,
568
commitAttachRef,
569
commitDetachRef,
@@ -814,6 +886,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
886
887
if (enableMutatingReconciler) {
888
return {
889
+ commitBeforeMutationLifeCycles,
890
commitResetTextContent,
891
commitPlacement,
892
commitDeletion,
packages/react-reconciler/src/ReactFiberScheduler.js
+8
@@ -21,6 +21,7 @@ import {
21
PerformedWork,
22
Placement,
23
Update,
24
+ Snapshot,
25
PlacementAndUpdate,
26
Deletion,
27
ContentReset,
@@ -197,6 +198,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
198
isAlreadyFailedLegacyErrorBoundary,
199
);
200
const {
201
+ commitBeforeMutationLifeCycles,
202
commitResetTextContent,
203
commitPlacement,
204
commitDeletion,
@@ -319,6 +321,12 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
321
recordEffect();
322
323
const effectTag = nextEffect.effectTag;
324
+
325
+ if (effectTag & Snapshot) {
326
+ const current = nextEffect.alternate;
327
+ commitBeforeMutationLifeCycles(current, nextEffect);
328
+ }
329
+
330
if (effectTag & ContentReset) {
331
commitResetTextContent(nextEffect);
332
}
packages/react-reconciler/src/ReactStrictModeWarnings.js
+4
-1
@@ -213,7 +213,10 @@ if (__DEV__) {
213
) {
214
pendingComponentWillReceivePropsWarnings.push(fiber);
215
}
216
- if (typeof instance.componentWillUpdate === 'function') {
216
+ if (
217
+ typeof instance.componentWillUpdate === 'function' &&
218
+ instance.componentWillUpdate.__suppressDeprecationWarning !== true
219
+ ) {
220
pendingComponentWillUpdateWarnings.push(fiber);
221
}
222
};
packages/react-test-renderer/src/ReactShallowRenderer.js
+32
-36
@@ -139,20 +139,18 @@ class ReactShallowRenderer {
139
) {
140
const beforeState = this._newState;
141
142
- if (typeof this._instance.componentWillMount === 'function') {
143
- // In order to support react-lifecycles-compat polyfilled components,
144
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
145
- if (typeof element.type.getDerivedStateFromProps !== 'function') {
146
- this._instance.componentWillMount();
147
- }
148
- }
142
+ // In order to support react-lifecycles-compat polyfilled components,
143
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
144
if (
150
- typeof this._instance.UNSAFE_componentWillMount === 'function' &&
151
- typeof element.type.getDerivedStateFromProps !== 'function'
145
+ typeof element.type.getDerivedStateFromProps !== 'function' &&
146
+ typeof this._instance.getSnapshotBeforeUpdate !== 'function'
147
) {
153
- // In order to support react-lifecycles-compat polyfilled components,
154
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
155
- this._instance.UNSAFE_componentWillMount();
148
+ if (typeof this._instance.componentWillMount === 'function') {
149
+ this._instance.componentWillMount();
150
+ }
151
+ if (typeof this._instance.UNSAFE_componentWillMount === 'function') {
152
+ this._instance.UNSAFE_componentWillMount();
153
+ }
154
}
155
156
// setState may have been called during cWM
@@ -173,20 +171,20 @@ class ReactShallowRenderer {
171
const oldProps = this._instance.props;
172
173
if (oldProps !== props) {
176
- if (typeof this._instance.componentWillReceiveProps === 'function') {
177
- // In order to support react-lifecycles-compat polyfilled components,
178
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
179
- if (typeof element.type.getDerivedStateFromProps !== 'function') {
180
- this._instance.componentWillReceiveProps(props, context);
181
- }
182
- }
174
+ // In order to support react-lifecycles-compat polyfilled components,
175
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
176
if (
184
- typeof this._instance.UNSAFE_componentWillReceiveProps === 'function' &&
185
- typeof element.type.getDerivedStateFromProps !== 'function'
177
+ typeof element.type.getDerivedStateFromProps !== 'function' &&
178
+ typeof this._instance.getSnapshotBeforeUpdate !== 'function'
179
) {
187
- // In order to support react-lifecycles-compat polyfilled components,
188
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
189
- this._instance.UNSAFE_componentWillReceiveProps(props, context);
180
+ if (typeof this._instance.componentWillReceiveProps === 'function') {
181
+ this._instance.componentWillReceiveProps(props, context);
182
+ }
183
+ if (
184
+ typeof this._instance.UNSAFE_componentWillReceiveProps === 'function'
185
+ ) {
186
+ this._instance.UNSAFE_componentWillReceiveProps(props, context);
187
+ }
188
}
189
190
this._updateStateFromStaticLifecycle(props);
@@ -211,20 +209,18 @@ class ReactShallowRenderer {
209
}
210
211
if (shouldUpdate) {
214
- if (typeof this._instance.componentWillUpdate === 'function') {
215
- // In order to support react-lifecycles-compat polyfilled components,
216
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
217
- if (typeof type.getDerivedStateFromProps !== 'function') {
218
- this._instance.componentWillUpdate(props, state, context);
219
- }
220
- }
212
+ // In order to support react-lifecycles-compat polyfilled components,
213
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
214
if (
222
- typeof this._instance.UNSAFE_componentWillUpdate === 'function' &&
223
- typeof type.getDerivedStateFromProps !== 'function'
215
+ typeof element.type.getDerivedStateFromProps !== 'function' &&
216
+ typeof this._instance.getSnapshotBeforeUpdate !== 'function'
217
) {
225
- // In order to support react-lifecycles-compat polyfilled components,
226
- // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
227
- this._instance.UNSAFE_componentWillUpdate(props, state, context);
218
+ if (typeof this._instance.componentWillUpdate === 'function') {
219
+ this._instance.componentWillUpdate(props, state, context);
220
+ }
221
+ if (typeof this._instance.UNSAFE_componentWillUpdate === 'function') {
222
+ this._instance.UNSAFE_componentWillUpdate(props, state, context);
223
+ }
224
}
225
}
226
packages/react-test-renderer/src/__tests__/ReactShallowRenderer-test.js
+42
@@ -128,6 +128,48 @@ describe('ReactShallowRenderer', () => {
128
shallowRenderer.render(<Component />);
129
});
130
131
+ it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => {
132
+ class Component extends React.Component {
133
+ getSnapshotBeforeUpdate() {
134
+ return null;
135
+ }
136
+ componentWillMount() {
137
+ throw Error('unexpected');
138
+ }
139
+ componentWillReceiveProps() {
140
+ throw Error('unexpected');
141
+ }
142
+ componentWillUpdate() {
143
+ throw Error('unexpected');
144
+ }
145
+ render() {
146
+ return null;
147
+ }
148
+ }
149
+
150
+ const shallowRenderer = createRenderer();
151
+ shallowRenderer.render(<Component value={1} />);
152
+ shallowRenderer.render(<Component value={2} />);
153
+ });
154
+
155
+ it('should not call getSnapshotBeforeUpdate or componentDidUpdate when updating since refs wont exist', () => {
156
+ class Component extends React.Component {
157
+ getSnapshotBeforeUpdate() {
158
+ throw Error('unexpected');
159
+ }
160
+ componentDidUpdate() {
161
+ throw Error('unexpected');
162
+ }
163
+ render() {
164
+ return null;
165
+ }
166
+ }
167
+
168
+ const shallowRenderer = createRenderer();
169
+ shallowRenderer.render(<Component value={1} />);
170
+ shallowRenderer.render(<Component value={2} />);
171
+ });
172
+
173
it('should only render 1 level deep', () => {
174
function Parent() {
175
return (
packages/react/src/__tests__/ReactContextValidator-test.js
-37
@@ -119,43 +119,6 @@ describe('ReactContextValidator', () => {
119
expect(actualComponentWillUpdate).toEqual({foo: 'def'});
120
});
121
122
- it('should not pass previous context to lifecycles', () => {
123
- let actualComponentDidUpdate;
124
-
125
- class Parent extends React.Component {
126
- getChildContext() {
127
- return {
128
- foo: this.props.foo,
129
- };
130
- }
131
-
132
- render() {
133
- return <Component />;
134
- }
135
- }
136
- Parent.childContextTypes = {
137
- foo: PropTypes.string.isRequired,
138
- };
139
-
140
- class Component extends React.Component {
141
- componentDidUpdate(...args) {
142
- actualComponentDidUpdate = args;
143
- }
144
-
145
- render() {
146
- return <div />;
147
- }
148
- }
149
- Component.contextTypes = {
150
- foo: PropTypes.string,
151
- };
152
-
153
- const container = document.createElement('div');
154
- ReactDOM.render(<Parent foo="abc" />, container);
155
- ReactDOM.render(<Parent foo="def" />, container);
156
- expect(actualComponentDidUpdate).toHaveLength(2);
157
- });
158
-
122
it('should check context types', () => {
123
class Component extends React.Component {
124
render() {
packages/react/src/__tests__/createReactClassIntegration-test.js
+35
-1
@@ -510,7 +510,7 @@ describe('create-react-class-integration', () => {
510
expect(() => {
511
ReactDOM.render(<Component />, document.createElement('div'));
512
}).toWarnDev(
513
- 'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
513
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
514
'Component uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
515
' componentWillMount\n' +
516
' componentWillReceiveProps\n' +
@@ -521,6 +521,40 @@ describe('create-react-class-integration', () => {
521
ReactDOM.render(<Component foo={1} />, document.createElement('div'));
522
});
523
524
+ it('should not invoke deprecated lifecycles (cWM/cWRP/cWU) if new getSnapshotBeforeUpdate is present', () => {
525
+ const Component = createReactClass({
526
+ getSnapshotBeforeUpdate: function() {
527
+ return null;
528
+ },
529
+ componentWillMount: function() {
530
+ throw Error('unexpected');
531
+ },
532
+ componentWillReceiveProps: function() {
533
+ throw Error('unexpected');
534
+ },
535
+ componentWillUpdate: function() {
536
+ throw Error('unexpected');
537
+ },
538
+ componentDidUpdate: function() {},
539
+ render: function() {
540
+ return null;
541
+ },
542
+ });
543
+
544
+ expect(() => {
545
+ ReactDOM.render(<Component />, document.createElement('div'));
546
+ }).toWarnDev(
547
+ 'Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' +
548
+ 'Component uses getSnapshotBeforeUpdate() but also contains the following legacy lifecycles:\n' +
549
+ ' componentWillMount\n' +
550
+ ' componentWillReceiveProps\n' +
551
+ ' componentWillUpdate\n\n' +
552
+ 'The above lifecycles should be removed. Learn more about this warning here:\n' +
553
+ 'https://fb.me/react-async-component-lifecycle-hooks',
554
+ );
555
+ ReactDOM.render(<Component foo={1} />, document.createElement('div'));
556
+ });
557
+
558
it('should invoke both deprecated and new lifecycles if both are present', () => {
559
const log = [];
560
packages/shared/ReactTypeOfSideEffect.js
+15
-14
@@ -10,22 +10,23 @@
10
export type TypeOfSideEffect = number;
11
12
// Don't change these two values. They're used by React Dev Tools.
13
-export const NoEffect = /* */ 0b00000000000;
14
-export const PerformedWork = /* */ 0b00000000001;
13
+export const NoEffect = /* */ 0b000000000000;
14
+export const PerformedWork = /* */ 0b000000000001;
15
16
// You can change the rest (and add more).
17
-export const Placement = /* */ 0b00000000010;
18
-export const Update = /* */ 0b00000000100;
19
-export const PlacementAndUpdate = /* */ 0b00000000110;
20
-export const Deletion = /* */ 0b00000001000;
21
-export const ContentReset = /* */ 0b00000010000;
22
-export const Callback = /* */ 0b00000100000;
23
-export const DidCapture = /* */ 0b00001000000;
24
-export const Ref = /* */ 0b00010000000;
25
-export const ErrLog = /* */ 0b00100000000;
17
+export const Placement = /* */ 0b000000000010;
18
+export const Update = /* */ 0b000000000100;
19
+export const PlacementAndUpdate = /* */ 0b000000000110;
20
+export const Deletion = /* */ 0b000000001000;
21
+export const ContentReset = /* */ 0b000000010000;
22
+export const Callback = /* */ 0b000000100000;
23
+export const DidCapture = /* */ 0b000001000000;
24
+export const Ref = /* */ 0b000010000000;
25
+export const ErrLog = /* */ 0b000100000000;
26
+export const Snapshot = /* */ 0b100000000000;
27
28
// Union of all host effects
28
-export const HostEffectMask = /* */ 0b00111111111;
29
+export const HostEffectMask = /* */ 0b100111111111;
30
30
-export const Incomplete = /* */ 0b01000000000;
31
-export const ShouldCapture = /* */ 0b10000000000;
31
+export const Incomplete = /* */ 0b001000000000;
32
+export const ShouldCapture = /* */ 0b010000000000;