Put recent change to getDerivedStateFromProps behind a feature flag (#12788)
This will allow us to safely ship it at Facebook and get a better idea for if/how it breaks existing product code.
Andrew Clark committed
May 11, 2018 at 18:45 UTC
4b2e65d32e3adec7d4ef5ddd5fcb35e617e0cde6
10 files changed
+64
-6
packages/react-reconciler/src/ReactFiberClassComponent.js
+9
-6
@@ -16,6 +16,7 @@ import {
16
debugRenderPhaseSideEffects,
17
debugRenderPhaseSideEffectsForStrictMode,
18
warnAboutDeprecatedLifecycles,
19
+ fireGetDerivedStateFromPropsOnStateUpdates,
20
} from 'shared/ReactFeatureFlags';
21
import ReactStrictModeWarnings from './ReactStrictModeWarnings';
22
import {isMounted} from 'react-reconciler/reflection';
@@ -937,12 +938,14 @@ export default function(
938
}
939
940
if (typeof getDerivedStateFromProps === 'function') {
940
- applyDerivedStateFromProps(
941
- workInProgress,
942
- getDerivedStateFromProps,
943
- newProps,
944
- );
945
- newState = workInProgress.memoizedState;
941
+ if (fireGetDerivedStateFromPropsOnStateUpdates || oldProps !== newProps) {
942
+ applyDerivedStateFromProps(
943
+ workInProgress,
944
+ getDerivedStateFromProps,
945
+ newProps,
946
+ );
947
+ newState = workInProgress.memoizedState;
948
+ }
949
}
950
951
if (
packages/react-reconciler/src/__tests__/ReactIncremental-test.internal.js
+45
@@ -1464,6 +1464,51 @@ describe('ReactIncremental', () => {
1464
expect(instance.state).toEqual({foo: 'foo'});
1465
});
1466
1467
+ it('does not call getDerivedStateFromProps for state-only updates if feature flag is disabled', () => {
1468
+ jest.resetModules();
1469
+ ReactFeatureFlags = require('shared/ReactFeatureFlags');
1470
+ ReactFeatureFlags.debugRenderPhaseSideEffectsForStrictMode = false;
1471
+ ReactFeatureFlags.fireGetDerivedStateFromPropsOnStateUpdates = false;
1472
+ React = require('react');
1473
+ ReactNoop = require('react-noop-renderer');
1474
+
1475
+ let ops = [];
1476
+ let instance;
1477
+
1478
+ class LifeCycle extends React.Component {
1479
+ state = {};
1480
+ static getDerivedStateFromProps(props, prevState) {
1481
+ ops.push('getDerivedStateFromProps');
1482
+ return {foo: 'foo'};
1483
+ }
1484
+ changeState() {
1485
+ this.setState({foo: 'bar'});
1486
+ }
1487
+ componentDidUpdate() {
1488
+ ops.push('componentDidUpdate');
1489
+ }
1490
+ render() {
1491
+ ops.push('render');
1492
+ instance = this;
1493
+ return null;
1494
+ }
1495
+ }
1496
+
1497
+ ReactNoop.render(<LifeCycle />);
1498
+ ReactNoop.flush();
1499
+
1500
+ expect(ops).toEqual(['getDerivedStateFromProps', 'render']);
1501
+ expect(instance.state).toEqual({foo: 'foo'});
1502
+
1503
+ ops = [];
1504
+
1505
+ instance.changeState();
1506
+ ReactNoop.flush();
1507
+
1508
+ expect(ops).toEqual(['render', 'componentDidUpdate']);
1509
+ expect(instance.state).toEqual({foo: 'bar'});
1510
+ });
1511
+
1512
xit('does not call componentWillReceiveProps for state-only updates', () => {
1513
let ops = [];
1514
packages/shared/ReactFeatureFlags.js
+3
@@ -42,6 +42,9 @@ export const warnAboutDeprecatedLifecycles = false;
42
// Gather advanced timing metrics for Profiler subtrees.
43
export const enableProfilerTimer = false;
44
45
+// Fires getDerivedStateFromProps for state *or* props changes
46
+export const fireGetDerivedStateFromPropsOnStateUpdates = true;
47
+
48
// Only used in www builds.
49
export function addUserTimingListener() {
50
invariant(false, 'Not implemented.');
packages/shared/forks/ReactFeatureFlags.native-fabric-fb.js
+1
@@ -25,6 +25,7 @@ export const enableProfilerTimer = __DEV__;
25
export const enableMutatingReconciler = false;
26
export const enableNoopReconciler = false;
27
export const enablePersistentReconciler = true;
28
+export const fireGetDerivedStateFromPropsOnStateUpdates = true;
29
30
// Only used in www builds.
31
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-fabric-oss.js
+1
@@ -25,6 +25,7 @@ export const enableProfilerTimer = false;
25
export const enableMutatingReconciler = false;
26
export const enableNoopReconciler = false;
27
export const enablePersistentReconciler = true;
28
+export const fireGetDerivedStateFromPropsOnStateUpdates = true;
29
30
// Only used in www builds.
31
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -21,6 +21,7 @@ export const {
21
warnAboutDeprecatedLifecycles,
22
replayFailedUnitOfWorkWithInvokeGuardedCallback,
23
enableProfilerTimer,
24
+ fireGetDerivedStateFromPropsOnStateUpdates,
25
} = require('ReactFeatureFlags');
26
27
// The rest of the flags are static for better dead code elimination.
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -23,6 +23,7 @@ export const enableUserTimingAPI = __DEV__;
23
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
24
export const warnAboutDeprecatedLifecycles = false;
25
export const enableProfilerTimer = false;
26
+export const fireGetDerivedStateFromPropsOnStateUpdates = true;
27
28
// Only used in www builds.
29
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.persistent.js
+1
@@ -20,6 +20,7 @@ export const enableSuspense = false;
20
export const warnAboutDeprecatedLifecycles = false;
21
export const replayFailedUnitOfWorkWithInvokeGuardedCallback = __DEV__;
22
export const enableProfilerTimer = false;
23
+export const fireGetDerivedStateFromPropsOnStateUpdates = true;
24
25
// react-reconciler/persistent entry point
26
// uses a persistent reconciler.
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -23,6 +23,7 @@ export const enableMutatingReconciler = true;
23
export const enableNoopReconciler = false;
24
export const enablePersistentReconciler = false;
25
export const enableProfilerTimer = false;
26
+export const fireGetDerivedStateFromPropsOnStateUpdates = true;
27
28
// Only used in www builds.
29
export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.www.js
+1
@@ -19,6 +19,7 @@ export const {
19
replayFailedUnitOfWorkWithInvokeGuardedCallback,
20
warnAboutDeprecatedLifecycles,
21
enableProfilerTimer,
22
+ fireGetDerivedStateFromPropsOnStateUpdates,
23
} = require('ReactFeatureFlags');
24
25
// The rest of the flags are static for better dead code elimination.