@samitouri / QOS-React / commits / 4b2e65d32e

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.