@samitouri / QOS-React / commits / e1a106a071

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;