mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
RFC 6: Deprecate unsafe lifecycles (#12028)
* Added unsafe_* lifecycles and deprecation warnings If the old lifecycle hooks (componentWillMount, componentWillUpdate, componentWillReceiveProps) are detected, these methods will be called and a deprecation warning will be logged. (In other words, we do not check for both the presence of the old and new lifecycles.) This commit is expected to fail tests. * Ran lifecycle hook codemod over project This should handle the bulk of the updates. I will manually update TypeScript and CoffeeScript tests with another commit. The actual command run with this commit was: jscodeshift --parser=flow -t ../react-codemod/transforms/rename-unsafe-lifecycles.js ./packages/**/src/**/*.js * Manually migrated CoffeeScript and TypeScript tests * Added inline note to createReactClassIntegration-test Explaining why lifecycles hooks have not been renamed in this test. * Udated NativeMethodsMixin with new lifecycle hooks * Added static getDerivedStateFromProps to ReactPartialRenderer Also added a new set of tests focused on server side lifecycle hooks. * Added getDerivedStateFromProps to shallow renderer Also added warnings for several cases involving getDerivedStateFromProps() as well as the deprecated lifecycles. Also added tests for the above. * Dedupe and DEV-only deprecation warning in server renderer * Renamed unsafe_* prefix to UNSAFE_* to be more noticeable * Added getDerivedStateFromProps to ReactFiberClassComponent Also updated class component and lifecyle tests to cover the added functionality. * Warn about UNSAFE_componentWillRecieveProps misspelling * Added tests to createReactClassIntegration for new lifecycles * Added warning for stateless functional components with gDSFP * Added createReactClass test for static gDSFP * Moved lifecycle deprecation warnings behind (disabled) feature flag Updated tests accordingly, by temporarily splitting tests that were specific to this feature-flag into their own, internal tests. This was the only way I knew of to interact with the feature flag without breaking our build/dist tests. * Tidying up * Tweaked warning message wording slightly Replaced 'You may may have returned undefined.' with 'You may have returned undefined.' * Replaced truthy partialState checks with != null * Call getDerivedStateFromProps via .call(null) to prevent type access * Move shallow-renderer didWarn* maps off the instance * Only call getDerivedStateFromProps if props instance has changed * Avoid creating new state object if not necessary * Inject state as a param to callGetDerivedStateFromProps This value will be either workInProgress.memoizedState (for updates) or instance.state (for initialization). * Explicitly warn about uninitialized state before calling getDerivedStateFromProps. And added some new tests for this change. Also: * Improved a couple of falsy null/undefined checks to more explicitly check for null or undefined. * Made some small tweaks to ReactFiberClassComponent WRT when and how it reads instance.state and sets to null. * Improved wording for deprecation lifecycle warnings * Fix state-regression for module-pattern components Also add support for new static getDerivedStateFromProps method
This commit is contained in:
@@ -275,7 +275,7 @@ describe('ReactCallReturn', () => {
|
||||
return ReactCallReturn.unstable_createReturn(this.props.children);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
ops.push(`Mount Return ${this.props.value}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
describe('ReactComponentLifeCycle', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.warnAboutDeprecatedLifecycles = true;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
});
|
||||
|
||||
// TODO (RFC #6) Merge this back into ReactComponentLifeCycles-test once
|
||||
// the 'warnAboutDeprecatedLifecycles' feature flag has been removed.
|
||||
it('warns about deprecated unsafe lifecycles', function() {
|
||||
class MyComponent extends React.Component {
|
||||
componentWillMount() {}
|
||||
componentWillReceiveProps() {}
|
||||
componentWillUpdate() {}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
expect(() => ReactDOM.render(<MyComponent x={1} />, container)).toWarnDev([
|
||||
'Warning: MyComponent: componentWillMount() is deprecated and will be ' +
|
||||
'removed in the next major version.',
|
||||
]);
|
||||
|
||||
expect(() => ReactDOM.render(<MyComponent x={2} />, container)).toWarnDev([
|
||||
'Warning: MyComponent: componentWillReceiveProps() is deprecated and ' +
|
||||
'will be removed in the next major version.',
|
||||
'Warning: MyComponent: componentWillUpdate() is deprecated and will be ' +
|
||||
'removed in the next major version.',
|
||||
]);
|
||||
|
||||
// Dedupe check (instantiate and update)
|
||||
ReactDOM.render(<MyComponent key="new" x={1} />, container);
|
||||
ReactDOM.render(<MyComponent key="new" x={2} />, container);
|
||||
});
|
||||
});
|
||||
@@ -165,7 +165,7 @@ describe('ReactComponentLifeCycle', () => {
|
||||
// had provided a getInitialState method.
|
||||
it('throws when accessing state in componentWillMount', () => {
|
||||
class StatefulComponent extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
void this.state.yada;
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ describe('ReactComponentLifeCycle', () => {
|
||||
|
||||
it('should allow update state inside of componentWillMount', () => {
|
||||
class StatefulComponent extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({stateField: 'something'});
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ describe('ReactComponentLifeCycle', () => {
|
||||
// reaching into the updater.
|
||||
return this.updater.isMounted(this);
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
expect(this._isMounted()).toBeFalsy();
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -258,7 +258,7 @@ describe('ReactComponentLifeCycle', () => {
|
||||
// reaching into the updater.
|
||||
return this.updater.isMounted(this);
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
expect(this._isMounted()).toBeFalsy();
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -334,7 +334,7 @@ describe('ReactComponentLifeCycle', () => {
|
||||
this.state = initState;
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this._testJournal.stateAtStartOfWillMount = clone(this.state);
|
||||
this._testJournal.lifeCycleAtStartOfWillMount = getLifeCycleState(this);
|
||||
this.state.hasWillMountCompleted = true;
|
||||
@@ -509,11 +509,18 @@ describe('ReactComponentLifeCycle', () => {
|
||||
};
|
||||
};
|
||||
class Outer extends React.Component {
|
||||
componentWillMount = logger('outer componentWillMount');
|
||||
state = {};
|
||||
static getDerivedStateFromProps(props, prevState) {
|
||||
log.push('outer getDerivedStateFromProps');
|
||||
return null;
|
||||
}
|
||||
UNSAFE_componentWillMount = logger('outer componentWillMount');
|
||||
componentDidMount = logger('outer componentDidMount');
|
||||
componentWillReceiveProps = logger('outer componentWillReceiveProps');
|
||||
UNSAFE_componentWillReceiveProps = logger(
|
||||
'outer componentWillReceiveProps',
|
||||
);
|
||||
shouldComponentUpdate = logger('outer shouldComponentUpdate');
|
||||
componentWillUpdate = logger('outer componentWillUpdate');
|
||||
UNSAFE_componentWillUpdate = logger('outer componentWillUpdate');
|
||||
componentDidUpdate = logger('outer componentDidUpdate');
|
||||
componentWillUnmount = logger('outer componentWillUnmount');
|
||||
render() {
|
||||
@@ -526,11 +533,18 @@ describe('ReactComponentLifeCycle', () => {
|
||||
}
|
||||
|
||||
class Inner extends React.Component {
|
||||
componentWillMount = logger('inner componentWillMount');
|
||||
state = {};
|
||||
static getDerivedStateFromProps(props, prevState) {
|
||||
log.push('inner getDerivedStateFromProps');
|
||||
return null;
|
||||
}
|
||||
UNSAFE_componentWillMount = logger('inner componentWillMount');
|
||||
componentDidMount = logger('inner componentDidMount');
|
||||
componentWillReceiveProps = logger('inner componentWillReceiveProps');
|
||||
UNSAFE_componentWillReceiveProps = logger(
|
||||
'inner componentWillReceiveProps',
|
||||
);
|
||||
shouldComponentUpdate = logger('inner shouldComponentUpdate');
|
||||
componentWillUpdate = logger('inner componentWillUpdate');
|
||||
UNSAFE_componentWillUpdate = logger('inner componentWillUpdate');
|
||||
componentDidUpdate = logger('inner componentDidUpdate');
|
||||
componentWillUnmount = logger('inner componentWillUnmount');
|
||||
render() {
|
||||
@@ -540,21 +554,33 @@ describe('ReactComponentLifeCycle', () => {
|
||||
|
||||
const container = document.createElement('div');
|
||||
log = [];
|
||||
ReactDOM.render(<Outer x={17} />, container);
|
||||
expect(() => ReactDOM.render(<Outer x={1} />, container)).toWarnDev([
|
||||
'Warning: Outer: Defines both componentWillReceiveProps() and static ' +
|
||||
'getDerivedStateFromProps() methods. ' +
|
||||
'We recommend using only getDerivedStateFromProps().',
|
||||
'Warning: Inner: Defines both componentWillReceiveProps() and static ' +
|
||||
'getDerivedStateFromProps() methods. ' +
|
||||
'We recommend using only getDerivedStateFromProps().',
|
||||
]);
|
||||
expect(log).toEqual([
|
||||
'outer getDerivedStateFromProps',
|
||||
'outer componentWillMount',
|
||||
'inner getDerivedStateFromProps',
|
||||
'inner componentWillMount',
|
||||
'inner componentDidMount',
|
||||
'outer componentDidMount',
|
||||
]);
|
||||
|
||||
// Dedup warnings
|
||||
log = [];
|
||||
ReactDOM.render(<Outer x={42} />, container);
|
||||
ReactDOM.render(<Outer x={2} />, container);
|
||||
expect(log).toEqual([
|
||||
'outer componentWillReceiveProps',
|
||||
'outer getDerivedStateFromProps',
|
||||
'outer shouldComponentUpdate',
|
||||
'outer componentWillUpdate',
|
||||
'inner componentWillReceiveProps',
|
||||
'inner getDerivedStateFromProps',
|
||||
'inner shouldComponentUpdate',
|
||||
'inner componentWillUpdate',
|
||||
'inner componentDidUpdate',
|
||||
@@ -579,7 +605,7 @@ describe('ReactComponentLifeCycle', () => {
|
||||
log.push('render');
|
||||
return <Child />;
|
||||
},
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('will mount');
|
||||
},
|
||||
componentDidMount() {
|
||||
@@ -619,4 +645,43 @@ describe('ReactComponentLifeCycle', () => {
|
||||
'ref',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should warn if getDerivedStateFromProps returns undefined', () => {
|
||||
class MyComponent extends React.Component {
|
||||
state = {};
|
||||
static getDerivedStateFromProps() {}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
expect(() => ReactDOM.render(<MyComponent />, div)).toWarnDev(
|
||||
'MyComponent.getDerivedStateFromProps(): A valid state object (or null) must ' +
|
||||
'be returned. You have returned undefined.',
|
||||
);
|
||||
|
||||
// De-duped
|
||||
ReactDOM.render(<MyComponent />, div);
|
||||
});
|
||||
|
||||
it('should warn if state is not initialized before getDerivedStateFromProps', () => {
|
||||
class MyComponent extends React.Component {
|
||||
static getDerivedStateFromProps() {
|
||||
return null;
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
expect(() => ReactDOM.render(<MyComponent />, div)).toWarnDev(
|
||||
'MyComponent: Did not properly initialize state during construction. ' +
|
||||
'Expected state to be an object, but it was undefined.',
|
||||
);
|
||||
|
||||
// De-duped
|
||||
ReactDOM.render(<MyComponent />, div);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -850,7 +850,7 @@ describe('ReactCompositeComponent', () => {
|
||||
foo: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
}
|
||||
|
||||
@@ -865,7 +865,7 @@ describe('ReactCompositeComponent', () => {
|
||||
}
|
||||
|
||||
class Intermediary extends React.Component {
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(false);
|
||||
}
|
||||
|
||||
@@ -916,7 +916,7 @@ describe('ReactCompositeComponent', () => {
|
||||
foo: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
@@ -938,7 +938,7 @@ describe('ReactCompositeComponent', () => {
|
||||
foo: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(true);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
@@ -956,7 +956,7 @@ describe('ReactCompositeComponent', () => {
|
||||
}
|
||||
|
||||
class ChildWithoutContext extends React.Component {
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
expect('foo' in nextContext).toBe(false);
|
||||
|
||||
if (nextProps !== this.props) {
|
||||
@@ -1047,7 +1047,7 @@ describe('ReactCompositeComponent', () => {
|
||||
class Component extends React.Component {
|
||||
state = {updated: false};
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
UNSAFE_componentWillReceiveProps(props) {
|
||||
expect(props.update).toBe(1);
|
||||
expect(renders).toBe(1);
|
||||
this.setState({updated: true});
|
||||
@@ -1075,7 +1075,7 @@ describe('ReactCompositeComponent', () => {
|
||||
class Component extends React.Component {
|
||||
state = {updated: false};
|
||||
|
||||
componentWillReceiveProps(props) {
|
||||
UNSAFE_componentWillReceiveProps(props) {
|
||||
expect(props.update).toBe(1);
|
||||
expect(renders).toBe(1);
|
||||
this.setState({updated: true});
|
||||
@@ -1377,7 +1377,7 @@ describe('ReactCompositeComponent', () => {
|
||||
const log = [];
|
||||
|
||||
class Spy extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push(this.props.name + ' componentWillMount');
|
||||
}
|
||||
render() {
|
||||
@@ -1556,7 +1556,7 @@ describe('ReactCompositeComponent', () => {
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState(
|
||||
{hasUpdatedState: true},
|
||||
() => (stateSuccessfullyUpdated = this.state.hasUpdatedState),
|
||||
@@ -1586,7 +1586,7 @@ describe('ReactCompositeComponent', () => {
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
instance = this;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ describe('ReactCompositeComponent-state', () => {
|
||||
return <div>{this.state.color}</div>;
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.peekAtState('componentWillMount-start');
|
||||
this.setState(function(state) {
|
||||
this.peekAtState('before-setState-sunrise', state);
|
||||
@@ -78,7 +78,7 @@ describe('ReactCompositeComponent-state', () => {
|
||||
this.peekAtState('componentDidMount-end');
|
||||
}
|
||||
|
||||
componentWillReceiveProps(newProps) {
|
||||
UNSAFE_componentWillReceiveProps(newProps) {
|
||||
this.peekAtState('componentWillReceiveProps-start');
|
||||
if (newProps.nextColor) {
|
||||
this.setState(function(state) {
|
||||
@@ -105,7 +105,7 @@ describe('ReactCompositeComponent-state', () => {
|
||||
return true;
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
UNSAFE_componentWillUpdate(nextProps, nextState) {
|
||||
this.peekAtState('componentWillUpdate-currentState');
|
||||
this.peekAtState('componentWillUpdate-nextState', nextState);
|
||||
}
|
||||
@@ -323,7 +323,7 @@ describe('ReactCompositeComponent-state', () => {
|
||||
}
|
||||
let updated = false;
|
||||
class Child extends React.Component {
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
if (updated) {
|
||||
return;
|
||||
}
|
||||
@@ -383,7 +383,7 @@ describe('ReactCompositeComponent-state', () => {
|
||||
let ops = [];
|
||||
class Test extends React.Component {
|
||||
state = {step: 1, extra: true};
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
this.setState({step: 2}, () => {
|
||||
// Tests that earlier setState callbacks are not dropped
|
||||
ops.push(
|
||||
@@ -426,7 +426,7 @@ describe('ReactCompositeComponent-state', () => {
|
||||
let ops = [];
|
||||
class Test extends React.Component {
|
||||
state = {step: 1, extra: true};
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({step: 2}, () => {
|
||||
// Tests that earlier setState callbacks are not dropped
|
||||
ops.push(
|
||||
@@ -458,4 +458,50 @@ describe('ReactCompositeComponent-state', () => {
|
||||
'callback -- step: 3, extra: false',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support stateful module pattern components', () => {
|
||||
function Child() {
|
||||
return {
|
||||
state: {
|
||||
count: 123,
|
||||
},
|
||||
render() {
|
||||
return <div>{`count:${this.state.count}`}</div>;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const el = document.createElement('div');
|
||||
ReactDOM.render(<Child />, el);
|
||||
|
||||
expect(el.textContent).toBe('count:123');
|
||||
});
|
||||
|
||||
it('should support getDerivedStateFromProps for module pattern components', () => {
|
||||
function Child() {
|
||||
return {
|
||||
state: {
|
||||
count: 1,
|
||||
},
|
||||
render() {
|
||||
return <div>{`count:${this.state.count}`}</div>;
|
||||
},
|
||||
};
|
||||
}
|
||||
Child.getDerivedStateFromProps = (props, prevState) => {
|
||||
return {
|
||||
count: prevState.count + props.incrementBy,
|
||||
};
|
||||
};
|
||||
|
||||
const el = document.createElement('div');
|
||||
ReactDOM.render(<Child incrementBy={0} />, el);
|
||||
expect(el.textContent).toBe('count:1');
|
||||
|
||||
ReactDOM.render(<Child incrementBy={2} />, el);
|
||||
expect(el.textContent).toBe('count:3');
|
||||
|
||||
ReactDOM.render(<Child incrementBy={1} />, el);
|
||||
expect(el.textContent).toBe('count:4');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -231,7 +231,7 @@ describe('ReactDOMServerIntegration', () => {
|
||||
getChildContext() {
|
||||
return {text: this.state.text};
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({text: 'foo'});
|
||||
}
|
||||
render() {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
let ReactDOMServer;
|
||||
|
||||
describe('ReactDOMServerLifecycles', () => {
|
||||
beforeEach(() => {
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.warnAboutDeprecatedLifecycles = true;
|
||||
|
||||
React = require('react');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
});
|
||||
|
||||
// TODO (RFC #6) Merge this back into ReactDOMServerLifecycles-test once
|
||||
// the 'warnAboutDeprecatedLifecycles' feature flag has been removed.
|
||||
it('should warn about deprecated lifecycle hooks', () => {
|
||||
class Component extends React.Component {
|
||||
componentWillMount() {}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => ReactDOMServer.renderToString(<Component />)).toWarnDev(
|
||||
'Warning: Component: componentWillMount() is deprecated and will be removed ' +
|
||||
'in the next major version.',
|
||||
);
|
||||
|
||||
// De-duped
|
||||
ReactDOMServer.renderToString(<Component />);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,157 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');
|
||||
|
||||
let React;
|
||||
let ReactDOMServer;
|
||||
|
||||
function initModules() {
|
||||
// Reset warning cache.
|
||||
jest.resetModuleRegistry();
|
||||
React = require('react');
|
||||
ReactDOMServer = require('react-dom/server');
|
||||
|
||||
// Make them available to the helpers.
|
||||
return {
|
||||
ReactDOMServer,
|
||||
};
|
||||
}
|
||||
|
||||
const {resetModules} = ReactDOMServerIntegrationUtils(initModules);
|
||||
|
||||
describe('ReactDOMServerLifecycles', () => {
|
||||
beforeEach(() => {
|
||||
resetModules();
|
||||
});
|
||||
|
||||
it('should invoke the correct lifecycle hooks', () => {
|
||||
const log = [];
|
||||
|
||||
class Outer extends React.Component {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('outer componentWillMount');
|
||||
}
|
||||
render() {
|
||||
log.push('outer render');
|
||||
return <Inner />;
|
||||
}
|
||||
}
|
||||
|
||||
class Inner extends React.Component {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('inner componentWillMount');
|
||||
}
|
||||
render() {
|
||||
log.push('inner render');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ReactDOMServer.renderToString(<Outer />);
|
||||
expect(log).toEqual([
|
||||
'outer componentWillMount',
|
||||
'outer render',
|
||||
'inner componentWillMount',
|
||||
'inner render',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should update instance.state with value returned from getDerivedStateFromProps', () => {
|
||||
class Grandparent extends React.Component {
|
||||
state = {
|
||||
foo: 'foo',
|
||||
};
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{`Grandparent: ${this.state.foo}`}
|
||||
<Parent />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends React.Component {
|
||||
state = {
|
||||
bar: 'bar',
|
||||
baz: 'baz',
|
||||
};
|
||||
static getDerivedStateFromProps(props, prevState) {
|
||||
return {
|
||||
bar: `not ${prevState.bar}`,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{`Parent: ${this.state.bar}, ${this.state.baz}`}
|
||||
<Child />;
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Child extends React.Component {
|
||||
state = {};
|
||||
static getDerivedStateFromProps() {
|
||||
return {
|
||||
qux: 'qux',
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return `Child: ${this.state.qux}`;
|
||||
}
|
||||
}
|
||||
|
||||
const markup = ReactDOMServer.renderToString(<Grandparent />);
|
||||
expect(markup).toContain('Grandparent: foo');
|
||||
expect(markup).toContain('Parent: not bar, baz');
|
||||
expect(markup).toContain('Child: qux');
|
||||
});
|
||||
|
||||
it('should warn if getDerivedStateFromProps returns undefined', () => {
|
||||
class Component extends React.Component {
|
||||
state = {};
|
||||
static getDerivedStateFromProps() {}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => ReactDOMServer.renderToString(<Component />)).toWarnDev(
|
||||
'Component.getDerivedStateFromProps(): A valid state object (or null) must ' +
|
||||
'be returned. You have returned undefined.',
|
||||
);
|
||||
|
||||
// De-duped
|
||||
ReactDOMServer.renderToString(<Component />);
|
||||
});
|
||||
|
||||
it('should warn if state is not initialized before getDerivedStateFromProps', () => {
|
||||
class Component extends React.Component {
|
||||
static getDerivedStateFromProps() {
|
||||
return null;
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => ReactDOMServer.renderToString(<Component />)).toWarnDev(
|
||||
'Component: Did not properly initialize state during construction. ' +
|
||||
'Expected state to be an object, but it was undefined.',
|
||||
);
|
||||
|
||||
// De-duped
|
||||
ReactDOMServer.renderToString(<Component />);
|
||||
});
|
||||
});
|
||||
+38
-38
@@ -50,16 +50,16 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenConstructor render');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenConstructor componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push('BrokenConstructor componentDidMount');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push('BrokenConstructor componentWillReceiveProps');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('BrokenConstructor componentWillUpdate');
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -79,17 +79,17 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenComponentWillMount render');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenComponentWillMount componentWillMount [!]');
|
||||
throw new Error('Hello');
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push('BrokenComponentWillMount componentDidMount');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push('BrokenComponentWillMount componentWillReceiveProps');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('BrokenComponentWillMount componentWillUpdate');
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -109,17 +109,17 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenComponentDidMount render');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenComponentDidMount componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push('BrokenComponentDidMount componentDidMount [!]');
|
||||
throw new Error('Hello');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push('BrokenComponentDidMount componentWillReceiveProps');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('BrokenComponentDidMount componentWillUpdate');
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -139,19 +139,19 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenComponentWillReceiveProps render');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenComponentWillReceiveProps componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push('BrokenComponentWillReceiveProps componentDidMount');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push(
|
||||
'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',
|
||||
);
|
||||
throw new Error('Hello');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('BrokenComponentWillReceiveProps componentWillUpdate');
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -171,16 +171,16 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenComponentWillUpdate render');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenComponentWillUpdate componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push('BrokenComponentWillUpdate componentDidMount');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push('BrokenComponentWillUpdate componentWillReceiveProps');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('BrokenComponentWillUpdate componentWillUpdate [!]');
|
||||
throw new Error('Hello');
|
||||
}
|
||||
@@ -204,16 +204,16 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenComponentDidUpdate render');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenComponentDidUpdate componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push('BrokenComponentDidUpdate componentDidMount');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push('BrokenComponentDidUpdate componentWillReceiveProps');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('BrokenComponentDidUpdate componentWillUpdate');
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -237,16 +237,16 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenComponentWillUnmount render');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenComponentWillUnmount componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push('BrokenComponentWillUnmount componentDidMount');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push('BrokenComponentWillUnmount componentWillReceiveProps');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('BrokenComponentWillUnmount componentWillUpdate');
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -272,7 +272,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenComponentWillMountErrorBoundary render success');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push(
|
||||
'BrokenComponentWillMountErrorBoundary componentWillMount [!]',
|
||||
);
|
||||
@@ -304,7 +304,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenComponentDidMountErrorBoundary render success');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenComponentDidMountErrorBoundary componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -334,7 +334,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenRenderErrorBoundary render success');
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenRenderErrorBoundary componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -358,16 +358,16 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('BrokenRender render [!]');
|
||||
throw new Error('Hello');
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('BrokenRender componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push('BrokenRender componentDidMount');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push('BrokenRender componentWillReceiveProps');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('BrokenRender componentWillUpdate');
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -387,7 +387,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('NoopErrorBoundary render');
|
||||
return <BrokenRender />;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('NoopErrorBoundary componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -413,16 +413,16 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push(`${this.props.logName} render`);
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push(`${this.props.logName} componentWillMount`);
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push(`${this.props.logName} componentDidMount`);
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push(`${this.props.logName} componentWillReceiveProps`);
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push(`${this.props.logName} componentWillUpdate`);
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -451,16 +451,16 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push(`${this.props.logName} componentDidCatch`);
|
||||
this.setState({error});
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push(`${this.props.logName} componentWillMount`);
|
||||
}
|
||||
componentDidMount() {
|
||||
log.push(`${this.props.logName} componentDidMount`);
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push(`${this.props.logName} componentWillReceiveProps`);
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push(`${this.props.logName} componentWillUpdate`);
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -490,7 +490,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
log.push('RetryErrorBoundary render');
|
||||
return <BrokenRender />;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('RetryErrorBoundary componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -511,7 +511,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
super(props);
|
||||
log.push('ErrorMessage constructor');
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('ErrorMessage componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -703,7 +703,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
throw new Error('Hello');
|
||||
}
|
||||
}
|
||||
@@ -727,7 +727,7 @@ describe('ReactErrorBoundaries', () => {
|
||||
render() {
|
||||
return <div>{this.props.children}</div>;
|
||||
},
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
throw new Error('Hello');
|
||||
},
|
||||
};
|
||||
|
||||
@@ -330,7 +330,7 @@ describe('ReactMultiChild', () => {
|
||||
const log = [];
|
||||
|
||||
class Spy extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push(this.props.name + ' componentWillMount');
|
||||
}
|
||||
render() {
|
||||
|
||||
+11
-11
@@ -98,7 +98,7 @@ describe('ReactDOMServer', () => {
|
||||
this.state = {name: 'TestComponent'};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
lifecycle.push('componentWillMount');
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ describe('ReactDOMServer', () => {
|
||||
return <span>Component name: {this.state.name}</span>;
|
||||
}
|
||||
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
lifecycle.push('componentWillUpdate');
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ describe('ReactDOMServer', () => {
|
||||
lifecycle.push('shouldComponentUpdate');
|
||||
}
|
||||
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
lifecycle.push('componentWillReceiveProps');
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ describe('ReactDOMServer', () => {
|
||||
this.state = {name: 'TestComponent'};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
lifecycle.push('componentWillMount');
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ describe('ReactDOMServer', () => {
|
||||
return <span>Component name: {this.state.name}</span>;
|
||||
}
|
||||
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
lifecycle.push('componentWillUpdate');
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ describe('ReactDOMServer', () => {
|
||||
lifecycle.push('shouldComponentUpdate');
|
||||
}
|
||||
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
lifecycle.push('componentWillReceiveProps');
|
||||
}
|
||||
|
||||
@@ -297,7 +297,7 @@ describe('ReactDOMServer', () => {
|
||||
|
||||
it('allows setState in componentWillMount without using DOM', () => {
|
||||
class Component extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({text: 'hello, world'});
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ describe('ReactDOMServer', () => {
|
||||
this.state = {text: 'default state'};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({text: 'hello, world'});
|
||||
}
|
||||
|
||||
@@ -397,7 +397,7 @@ describe('ReactDOMServer', () => {
|
||||
}
|
||||
|
||||
class Component extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({text: 'hello, world'});
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ describe('ReactDOMServer', () => {
|
||||
|
||||
it('warns with a no-op when an async setState is triggered', () => {
|
||||
class Foo extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({text: 'hello'});
|
||||
setTimeout(() => {
|
||||
this.setState({text: 'error'});
|
||||
@@ -446,7 +446,7 @@ describe('ReactDOMServer', () => {
|
||||
|
||||
it('warns with a no-op when an async forceUpdate is triggered', () => {
|
||||
class Baz extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.forceUpdate();
|
||||
setTimeout(() => {
|
||||
this.forceUpdate();
|
||||
|
||||
@@ -98,6 +98,22 @@ describe('ReactStatelessComponent', () => {
|
||||
expect(el.textContent).toBe('mest');
|
||||
});
|
||||
|
||||
it('should warn for getDerivedStateFromProps on a functional component', () => {
|
||||
function StatelessComponentWithChildContext() {
|
||||
return null;
|
||||
}
|
||||
StatelessComponentWithChildContext.getDerivedStateFromProps = function() {};
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
expect(() =>
|
||||
ReactDOM.render(<StatelessComponentWithChildContext />, container),
|
||||
).toWarnDev(
|
||||
'StatelessComponentWithChildContext: Stateless ' +
|
||||
'functional components do not support getDerivedStateFromProps.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn for childContextTypes on a functional component', () => {
|
||||
function StatelessComponentWithChildContext(props) {
|
||||
return <div>{props.name}</div>;
|
||||
|
||||
+9
-9
@@ -399,7 +399,7 @@ describe('ReactUpdates', () => {
|
||||
let didUpdates = [];
|
||||
|
||||
const UpdateLoggingMixin = {
|
||||
componentWillUpdate: function() {
|
||||
UNSAFE_componentWillUpdate: function() {
|
||||
willUpdates.push(this.constructor.displayName);
|
||||
},
|
||||
componentDidUpdate: function() {
|
||||
@@ -723,7 +723,7 @@ describe('ReactUpdates', () => {
|
||||
return <div />;
|
||||
}
|
||||
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
x.go();
|
||||
}
|
||||
}
|
||||
@@ -746,7 +746,7 @@ describe('ReactUpdates', () => {
|
||||
class A extends React.Component {
|
||||
state = {x: 0};
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
a = this;
|
||||
}
|
||||
|
||||
@@ -756,7 +756,7 @@ describe('ReactUpdates', () => {
|
||||
}
|
||||
|
||||
class B extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
a.setState({x: 1});
|
||||
}
|
||||
|
||||
@@ -784,7 +784,7 @@ describe('ReactUpdates', () => {
|
||||
class A extends React.Component {
|
||||
state = {x: this.props.x};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
const newX = nextProps.x;
|
||||
this.setState({x: newX}, function() {
|
||||
// State should have updated by the time this callback gets called
|
||||
@@ -945,7 +945,7 @@ describe('ReactUpdates', () => {
|
||||
class Child extends React.Component {
|
||||
state = {updated: false};
|
||||
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
if (!once) {
|
||||
once = true;
|
||||
this.setState({updated: true});
|
||||
@@ -1100,7 +1100,7 @@ describe('ReactUpdates', () => {
|
||||
let ops = [];
|
||||
class Foo extends React.Component {
|
||||
state = {a: false, b: false};
|
||||
componentWillUpdate(_, nextState) {
|
||||
UNSAFE_componentWillUpdate(_, nextState) {
|
||||
if (!nextState.a) {
|
||||
this.setState({a: true});
|
||||
}
|
||||
@@ -1143,7 +1143,7 @@ describe('ReactUpdates', () => {
|
||||
let ops = [];
|
||||
class Foo extends React.Component {
|
||||
state = {a: false};
|
||||
componentWillUpdate(_, nextState) {
|
||||
UNSAFE_componentWillUpdate(_, nextState) {
|
||||
if (!nextState.a) {
|
||||
this.setState({a: true});
|
||||
}
|
||||
@@ -1316,7 +1316,7 @@ describe('ReactUpdates', () => {
|
||||
componentDidMount() {
|
||||
this.setState({step: 1});
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
this.setState({step: 2});
|
||||
}
|
||||
render() {
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ describe('findDOMNode', () => {
|
||||
|
||||
it('findDOMNode should not throw an error when called within a component that is not mounted', () => {
|
||||
class Bar extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
expect(ReactDOM.findDOMNode(this)).toBeNull();
|
||||
}
|
||||
|
||||
|
||||
+75
-3
@@ -19,6 +19,7 @@ import warning from 'fbjs/lib/warning';
|
||||
import checkPropTypes from 'prop-types/checkPropTypes';
|
||||
import describeComponentFrame from 'shared/describeComponentFrame';
|
||||
import {ReactDebugCurrentFrame} from 'shared/ReactGlobalSharedState';
|
||||
import {warnAboutDeprecatedLifecycles} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_CALL_TYPE,
|
||||
@@ -123,6 +124,9 @@ let didWarnDefaultTextareaValue = false;
|
||||
let didWarnInvalidOptionChildren = false;
|
||||
const didWarnAboutNoopUpdateForComponent = {};
|
||||
const didWarnAboutBadClass = {};
|
||||
const didWarnAboutDeprecatedWillMount = {};
|
||||
const didWarnAboutUndefinedDerivedState = {};
|
||||
const didWarnAboutUninitializedState = {};
|
||||
const valuePropNames = ['value', 'defaultValue'];
|
||||
const newlineEatingTags = {
|
||||
listing: true,
|
||||
@@ -421,6 +425,49 @@ function resolve(
|
||||
|
||||
if (shouldConstruct(Component)) {
|
||||
inst = new Component(element.props, publicContext, updater);
|
||||
|
||||
if (typeof Component.getDerivedStateFromProps === 'function') {
|
||||
if (__DEV__) {
|
||||
if (inst.state === null || inst.state === undefined) {
|
||||
const componentName = getComponentName(Component) || 'Unknown';
|
||||
if (!didWarnAboutUninitializedState[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: Did not properly initialize state during construction. ' +
|
||||
'Expected state to be an object, but it was %s.',
|
||||
componentName,
|
||||
inst.state === null ? 'null' : 'undefined',
|
||||
);
|
||||
didWarnAboutUninitializedState[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partialState = Component.getDerivedStateFromProps.call(
|
||||
null,
|
||||
element.props,
|
||||
inst.state,
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
if (partialState === undefined) {
|
||||
const componentName = getComponentName(Component) || 'Unknown';
|
||||
if (!didWarnAboutUndefinedDerivedState[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
|
||||
'You have returned undefined.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutUndefinedDerivedState[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (partialState != null) {
|
||||
inst.state = Object.assign({}, inst.state, partialState);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
if (
|
||||
@@ -457,8 +504,33 @@ function resolve(
|
||||
if (initialState === undefined) {
|
||||
inst.state = initialState = null;
|
||||
}
|
||||
if (inst.componentWillMount) {
|
||||
inst.componentWillMount();
|
||||
if (inst.UNSAFE_componentWillMount || inst.componentWillMount) {
|
||||
if (inst.componentWillMount) {
|
||||
if (__DEV__) {
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
const componentName = getComponentName(Component) || 'Unknown';
|
||||
|
||||
if (!didWarnAboutDeprecatedWillMount[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: componentWillMount() is deprecated and will be ' +
|
||||
'removed in the next major version. Read about the motivations ' +
|
||||
'behind this change: ' +
|
||||
'https://fb.me/react-async-component-lifecycle-hooks' +
|
||||
'\n\n' +
|
||||
'As a temporary workaround, you can rename to ' +
|
||||
'UNSAFE_componentWillMount instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutDeprecatedWillMount[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inst.componentWillMount();
|
||||
} else {
|
||||
inst.UNSAFE_componentWillMount();
|
||||
}
|
||||
if (queue.length) {
|
||||
oldQueue = queue;
|
||||
oldReplace = replace;
|
||||
@@ -476,7 +548,7 @@ function resolve(
|
||||
typeof partial === 'function'
|
||||
? partial.call(inst, nextState, element.props, publicContext)
|
||||
: partial;
|
||||
if (partialState) {
|
||||
if (partialState != null) {
|
||||
if (dontMutate) {
|
||||
dontMutate = false;
|
||||
nextState = Object.assign({}, nextState, partialState);
|
||||
|
||||
@@ -187,7 +187,9 @@ if (__DEV__) {
|
||||
const NativeMethodsMixin_DEV = (NativeMethodsMixin: any);
|
||||
invariant(
|
||||
!NativeMethodsMixin_DEV.componentWillMount &&
|
||||
!NativeMethodsMixin_DEV.componentWillReceiveProps,
|
||||
!NativeMethodsMixin_DEV.componentWillReceiveProps &&
|
||||
!NativeMethodsMixin_DEV.UNSAFE_componentWillMount &&
|
||||
!NativeMethodsMixin_DEV.UNSAFE_componentWillReceiveProps,
|
||||
'Do not override existing functions.',
|
||||
);
|
||||
NativeMethodsMixin_DEV.componentWillMount = function() {
|
||||
@@ -196,6 +198,12 @@ if (__DEV__) {
|
||||
NativeMethodsMixin_DEV.componentWillReceiveProps = function(newProps) {
|
||||
throwOnStylesProp(this, newProps);
|
||||
};
|
||||
NativeMethodsMixin_DEV.UNSAFE_componentWillMount = function() {
|
||||
throwOnStylesProp(this, this.props);
|
||||
};
|
||||
NativeMethodsMixin_DEV.UNSAFE_componentWillReceiveProps = function(newProps) {
|
||||
throwOnStylesProp(this, newProps);
|
||||
};
|
||||
}
|
||||
|
||||
export default NativeMethodsMixin;
|
||||
|
||||
+45
-4
@@ -60,12 +60,14 @@ import {
|
||||
import {NoWork, Never} from './ReactFiberExpirationTime';
|
||||
import {AsyncUpdates} from './ReactTypeOfInternalContext';
|
||||
|
||||
let warnedAboutStatelessRefs;
|
||||
let didWarnAboutBadClass;
|
||||
let didWarnAboutGetDerivedStateOnFunctionalComponent;
|
||||
let didWarnAboutStatelessRefs;
|
||||
|
||||
if (__DEV__) {
|
||||
warnedAboutStatelessRefs = {};
|
||||
didWarnAboutBadClass = {};
|
||||
didWarnAboutGetDerivedStateOnFunctionalComponent = {};
|
||||
didWarnAboutStatelessRefs = {};
|
||||
}
|
||||
|
||||
export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
|
||||
@@ -87,6 +89,7 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
|
||||
|
||||
const {
|
||||
adoptClassInstance,
|
||||
callGetDerivedStateFromProps,
|
||||
constructClassInstance,
|
||||
mountClassInstance,
|
||||
// resumeMountClassInstance,
|
||||
@@ -483,9 +486,30 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
|
||||
value !== null &&
|
||||
typeof value.render === 'function'
|
||||
) {
|
||||
const Component = workInProgress.type;
|
||||
|
||||
// Proceed under the assumption that this is a class instance
|
||||
workInProgress.tag = ClassComponent;
|
||||
|
||||
workInProgress.memoizedState =
|
||||
value.state !== null && value.state !== undefined ? value.state : null;
|
||||
|
||||
if (typeof Component.getDerivedStateFromProps === 'function') {
|
||||
const partialState = callGetDerivedStateFromProps(
|
||||
workInProgress,
|
||||
value,
|
||||
props,
|
||||
);
|
||||
|
||||
if (partialState !== null && partialState !== undefined) {
|
||||
workInProgress.memoizedState = Object.assign(
|
||||
{},
|
||||
workInProgress.memoizedState,
|
||||
partialState,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Push context providers early to prevent context stack mismatches.
|
||||
// During mounting we don't know the child context yet as the instance doesn't exist.
|
||||
// We will invalidate the child context in finishClassComponent() right after rendering.
|
||||
@@ -518,8 +542,8 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
|
||||
if (debugSource) {
|
||||
warningKey = debugSource.fileName + ':' + debugSource.lineNumber;
|
||||
}
|
||||
if (!warnedAboutStatelessRefs[warningKey]) {
|
||||
warnedAboutStatelessRefs[warningKey] = true;
|
||||
if (!didWarnAboutStatelessRefs[warningKey]) {
|
||||
didWarnAboutStatelessRefs[warningKey] = true;
|
||||
warning(
|
||||
false,
|
||||
'Stateless function components cannot be given refs. ' +
|
||||
@@ -529,6 +553,23 @@ export default function<T, P, I, TI, HI, PI, C, CC, CX, PL>(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof fn.getDerivedStateFromProps === 'function') {
|
||||
const componentName = getComponentName(workInProgress) || 'Unknown';
|
||||
|
||||
if (
|
||||
!didWarnAboutGetDerivedStateOnFunctionalComponent[componentName]
|
||||
) {
|
||||
warning(
|
||||
false,
|
||||
'%s: Stateless functional components do not support getDerivedStateFromProps.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutGetDerivedStateOnFunctionalComponent[
|
||||
componentName
|
||||
] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
reconcileChildren(current, workInProgress, value);
|
||||
memoizeProps(workInProgress, props);
|
||||
|
||||
+240
-17
@@ -14,6 +14,7 @@ import {Update} from 'shared/ReactTypeOfSideEffect';
|
||||
import {
|
||||
debugRenderPhaseSideEffects,
|
||||
enableAsyncSubtreeAPI,
|
||||
warnAboutDeprecatedLifecycles,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {isMounted} from 'react-reconciler/reflection';
|
||||
import * as ReactInstanceMap from 'shared/ReactInstanceMap';
|
||||
@@ -40,12 +41,27 @@ import {hasContextChanged} from './ReactFiberContext';
|
||||
const fakeInternalInstance = {};
|
||||
const isArray = Array.isArray;
|
||||
|
||||
let didWarnAboutLegacyWillMount;
|
||||
let didWarnAboutLegacyWillReceiveProps;
|
||||
let didWarnAboutLegacyWillUpdate;
|
||||
let didWarnAboutStateAssignmentForComponent;
|
||||
let didWarnAboutUndefinedDerivedState;
|
||||
let didWarnAboutUninitializedState;
|
||||
let didWarnAboutWillReceivePropsAndDerivedState;
|
||||
let warnOnInvalidCallback;
|
||||
|
||||
if (__DEV__) {
|
||||
const didWarnOnInvalidCallback = {};
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
didWarnAboutLegacyWillMount = {};
|
||||
didWarnAboutLegacyWillReceiveProps = {};
|
||||
didWarnAboutLegacyWillUpdate = {};
|
||||
}
|
||||
didWarnAboutStateAssignmentForComponent = {};
|
||||
didWarnAboutUndefinedDerivedState = {};
|
||||
didWarnAboutUninitializedState = {};
|
||||
didWarnAboutWillReceivePropsAndDerivedState = {};
|
||||
|
||||
const didWarnOnInvalidCallback = {};
|
||||
|
||||
warnOnInvalidCallback = function(callback: mixed, callerName: string) {
|
||||
if (callback === null || typeof callback === 'function') {
|
||||
@@ -319,6 +335,14 @@ export default function(
|
||||
'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?',
|
||||
name,
|
||||
);
|
||||
const noUnsafeComponentWillRecieveProps =
|
||||
typeof instance.UNSAFE_componentWillRecieveProps !== 'function';
|
||||
warning(
|
||||
noUnsafeComponentWillRecieveProps,
|
||||
'%s has a method called ' +
|
||||
'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?',
|
||||
name,
|
||||
);
|
||||
const hasMutatedProps = instance.props !== workInProgress.pendingProps;
|
||||
warning(
|
||||
instance.props === undefined || !hasMutatedProps,
|
||||
@@ -378,8 +402,50 @@ export default function(
|
||||
? getMaskedContext(workInProgress, unmaskedContext)
|
||||
: emptyObject;
|
||||
const instance = new ctor(props, context);
|
||||
const state =
|
||||
instance.state !== null && instance.state !== undefined
|
||||
? instance.state
|
||||
: null;
|
||||
adoptClassInstance(workInProgress, instance);
|
||||
|
||||
if (__DEV__) {
|
||||
if (
|
||||
typeof ctor.getDerivedStateFromProps === 'function' &&
|
||||
state === null
|
||||
) {
|
||||
const componentName = getComponentName(workInProgress) || 'Unknown';
|
||||
if (!didWarnAboutUninitializedState[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: Did not properly initialize state during construction. ' +
|
||||
'Expected state to be an object, but it was %s.',
|
||||
componentName,
|
||||
instance.state === null ? 'null' : 'undefined',
|
||||
);
|
||||
didWarnAboutUninitializedState[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workInProgress.memoizedState = state;
|
||||
|
||||
const partialState = callGetDerivedStateFromProps(
|
||||
workInProgress,
|
||||
instance,
|
||||
props,
|
||||
);
|
||||
|
||||
if (partialState !== null && partialState !== undefined) {
|
||||
// Render-phase updates (like this) should not be added to the update queue,
|
||||
// So that multiple render passes do not enqueue multiple updates.
|
||||
// Instead, just synchronously merge the returned state into the instance.
|
||||
workInProgress.memoizedState = Object.assign(
|
||||
{},
|
||||
workInProgress.memoizedState,
|
||||
partialState,
|
||||
);
|
||||
}
|
||||
|
||||
// Cache unmasked context so we can avoid recreating masked context unless necessary.
|
||||
// ReactFiberContext usually updates this cache but can't for newly-created instances.
|
||||
if (needsContext) {
|
||||
@@ -392,7 +458,32 @@ export default function(
|
||||
function callComponentWillMount(workInProgress, instance) {
|
||||
startPhaseTimer(workInProgress, 'componentWillMount');
|
||||
const oldState = instance.state;
|
||||
instance.componentWillMount();
|
||||
|
||||
if (typeof instance.componentWillMount === 'function') {
|
||||
if (__DEV__) {
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
const componentName = getComponentName(workInProgress) || 'Component';
|
||||
if (!didWarnAboutLegacyWillMount[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: componentWillMount() is deprecated and will be ' +
|
||||
'removed in the next major version. Read about the motivations ' +
|
||||
'behind this change: ' +
|
||||
'https://fb.me/react-async-component-lifecycle-hooks' +
|
||||
'\n\n' +
|
||||
'As a temporary workaround, you can rename to ' +
|
||||
'UNSAFE_componentWillMount instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutLegacyWillMount[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
instance.componentWillMount();
|
||||
} else {
|
||||
instance.UNSAFE_componentWillMount();
|
||||
}
|
||||
|
||||
stopPhaseTimer();
|
||||
|
||||
if (oldState !== instance.state) {
|
||||
@@ -415,14 +506,41 @@ export default function(
|
||||
newProps,
|
||||
newContext,
|
||||
) {
|
||||
startPhaseTimer(workInProgress, 'componentWillReceiveProps');
|
||||
const oldState = instance.state;
|
||||
instance.componentWillReceiveProps(newProps, newContext);
|
||||
stopPhaseTimer();
|
||||
if (typeof instance.componentWillReceiveProps === 'function') {
|
||||
if (__DEV__) {
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
const componentName = getComponentName(workInProgress) || 'Component';
|
||||
if (!didWarnAboutLegacyWillReceiveProps[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: componentWillReceiveProps() is deprecated and ' +
|
||||
'will be removed in the next major version. Use ' +
|
||||
'static getDerivedStateFromProps() instead. Read about the ' +
|
||||
'motivations behind this change: ' +
|
||||
'https://fb.me/react-async-component-lifecycle-hooks' +
|
||||
'\n\n' +
|
||||
'As a temporary workaround, you can rename to ' +
|
||||
'UNSAFE_componentWillReceiveProps instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutLegacyWillReceiveProps[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate an async bailout/interruption by invoking lifecycle twice.
|
||||
if (debugRenderPhaseSideEffects) {
|
||||
startPhaseTimer(workInProgress, 'componentWillReceiveProps');
|
||||
instance.componentWillReceiveProps(newProps, newContext);
|
||||
stopPhaseTimer();
|
||||
} else {
|
||||
startPhaseTimer(workInProgress, 'componentWillReceiveProps');
|
||||
instance.UNSAFE_componentWillReceiveProps(newProps, newContext);
|
||||
stopPhaseTimer();
|
||||
|
||||
// Simulate an async bailout/interruption by invoking lifecycle twice.
|
||||
if (debugRenderPhaseSideEffects) {
|
||||
instance.UNSAFE_componentWillReceiveProps(newProps, newContext);
|
||||
}
|
||||
}
|
||||
|
||||
if (instance.state !== oldState) {
|
||||
@@ -443,6 +561,58 @@ export default function(
|
||||
}
|
||||
}
|
||||
|
||||
function callGetDerivedStateFromProps(
|
||||
workInProgress: Fiber,
|
||||
instance: any,
|
||||
props: any,
|
||||
) {
|
||||
const {type} = workInProgress;
|
||||
|
||||
if (typeof type.getDerivedStateFromProps === 'function') {
|
||||
if (__DEV__) {
|
||||
if (
|
||||
typeof instance.componentWillReceiveProps === 'function' ||
|
||||
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
|
||||
) {
|
||||
const componentName = getComponentName(workInProgress) || 'Unknown';
|
||||
if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: Defines both componentWillReceiveProps() and static ' +
|
||||
'getDerivedStateFromProps() methods. We recommend using ' +
|
||||
'only getDerivedStateFromProps().',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const partialState = type.getDerivedStateFromProps.call(
|
||||
null,
|
||||
props,
|
||||
workInProgress.memoizedState,
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
if (partialState === undefined) {
|
||||
const componentName = getComponentName(workInProgress) || 'Unknown';
|
||||
if (!didWarnAboutUndefinedDerivedState[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
|
||||
'You have returned undefined.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutUndefinedDerivedState[componentName] = componentName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return partialState;
|
||||
}
|
||||
}
|
||||
|
||||
// Invokes the mount life-cycles on a previously never rendered instance.
|
||||
function mountClassInstance(
|
||||
workInProgress: Fiber,
|
||||
@@ -455,12 +625,11 @@ export default function(
|
||||
}
|
||||
|
||||
const instance = workInProgress.stateNode;
|
||||
const state = instance.state || null;
|
||||
const props = workInProgress.pendingProps;
|
||||
const unmaskedContext = getUnmaskedContext(workInProgress);
|
||||
|
||||
instance.props = props;
|
||||
instance.state = workInProgress.memoizedState = state;
|
||||
instance.state = workInProgress.memoizedState;
|
||||
instance.refs = emptyObject;
|
||||
instance.context = getMaskedContext(workInProgress, unmaskedContext);
|
||||
|
||||
@@ -473,7 +642,10 @@ export default function(
|
||||
workInProgress.internalContextTag |= AsyncUpdates;
|
||||
}
|
||||
|
||||
if (typeof instance.componentWillMount === 'function') {
|
||||
if (
|
||||
typeof instance.UNSAFE_componentWillMount === 'function' ||
|
||||
typeof instance.componentWillMount === 'function'
|
||||
) {
|
||||
callComponentWillMount(workInProgress, instance);
|
||||
// If we had additional state updates during this life-cycle, let's
|
||||
// process them now.
|
||||
@@ -619,7 +791,8 @@ export default function(
|
||||
// during componentDidUpdate we pass the "current" props.
|
||||
|
||||
if (
|
||||
typeof instance.componentWillReceiveProps === 'function' &&
|
||||
(typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
|
||||
typeof instance.componentWillReceiveProps === 'function') &&
|
||||
(oldProps !== newProps || oldContext !== newContext)
|
||||
) {
|
||||
callComponentWillReceiveProps(
|
||||
@@ -630,6 +803,15 @@ export default function(
|
||||
);
|
||||
}
|
||||
|
||||
let partialState;
|
||||
if (oldProps !== newProps) {
|
||||
partialState = callGetDerivedStateFromProps(
|
||||
workInProgress,
|
||||
instance,
|
||||
newProps,
|
||||
);
|
||||
}
|
||||
|
||||
// Compute the next state using the memoized state and the update queue.
|
||||
const oldState = workInProgress.memoizedState;
|
||||
// TODO: Previous state can be null.
|
||||
@@ -647,6 +829,16 @@ export default function(
|
||||
newState = oldState;
|
||||
}
|
||||
|
||||
if (partialState !== null && partialState !== undefined) {
|
||||
// Render-phase updates (like this) should not be added to the update queue,
|
||||
// So that multiple render passes do not enqueue multiple updates.
|
||||
// Instead, just synchronously merge the returned state into the instance.
|
||||
newState =
|
||||
newState === null || newState === undefined
|
||||
? partialState
|
||||
: Object.assign({}, newState, partialState);
|
||||
}
|
||||
|
||||
if (
|
||||
oldProps === newProps &&
|
||||
oldState === newState &&
|
||||
@@ -679,14 +871,44 @@ export default function(
|
||||
);
|
||||
|
||||
if (shouldUpdate) {
|
||||
if (typeof instance.componentWillUpdate === 'function') {
|
||||
startPhaseTimer(workInProgress, 'componentWillUpdate');
|
||||
instance.componentWillUpdate(newProps, newState, newContext);
|
||||
stopPhaseTimer();
|
||||
if (
|
||||
typeof instance.UNSAFE_componentWillUpdate === 'function' ||
|
||||
typeof instance.componentWillUpdate === 'function'
|
||||
) {
|
||||
if (typeof instance.componentWillUpdate === 'function') {
|
||||
if (__DEV__) {
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
const componentName =
|
||||
getComponentName(workInProgress) || 'Component';
|
||||
if (!didWarnAboutLegacyWillUpdate[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: componentWillUpdate() is deprecated and will be ' +
|
||||
'removed in the next major version. Read about the motivations ' +
|
||||
'behind this change: ' +
|
||||
'https://fb.me/react-async-component-lifecycle-hooks' +
|
||||
'\n\n' +
|
||||
'As a temporary workaround, you can rename to ' +
|
||||
'UNSAFE_componentWillUpdate instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutLegacyWillUpdate[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Simulate an async bailout/interruption by invoking lifecycle twice.
|
||||
if (debugRenderPhaseSideEffects) {
|
||||
startPhaseTimer(workInProgress, 'componentWillUpdate');
|
||||
instance.componentWillUpdate(newProps, newState, newContext);
|
||||
stopPhaseTimer();
|
||||
} else {
|
||||
startPhaseTimer(workInProgress, 'componentWillUpdate');
|
||||
instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
|
||||
stopPhaseTimer();
|
||||
|
||||
// Simulate an async bailout/interruption by invoking lifecycle twice.
|
||||
if (debugRenderPhaseSideEffects) {
|
||||
instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeof instance.componentDidUpdate === 'function') {
|
||||
@@ -721,6 +943,7 @@ export default function(
|
||||
|
||||
return {
|
||||
adoptClassInstance,
|
||||
callGetDerivedStateFromProps,
|
||||
constructClassInstance,
|
||||
mountClassInstance,
|
||||
// resumeMountClassInstance,
|
||||
|
||||
@@ -571,16 +571,16 @@ describe('ReactIncremental', () => {
|
||||
ops.push('constructor: ' + props.prop);
|
||||
constructorCount++;
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
ops.push('componentWillMount: ' + this.props.prop);
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
ops.push('componentWillReceiveProps: ' + this.props.prop);
|
||||
}
|
||||
componentDidMount() {
|
||||
ops.push('componentDidMount: ' + this.props.prop);
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
ops.push('componentWillUpdate: ' + this.props.prop);
|
||||
}
|
||||
componentDidUpdate() {
|
||||
@@ -1232,13 +1232,13 @@ describe('ReactIncremental', () => {
|
||||
|
||||
class LifeCycle extends React.Component {
|
||||
state = {x: this.props.x};
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
ops.push(
|
||||
'componentWillReceiveProps:' + this.state.x + '-' + nextProps.x,
|
||||
);
|
||||
this.setState({x: nextProps.x});
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
ops.push('componentWillMount:' + this.state.x + '-' + this.props.x);
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -1291,7 +1291,7 @@ describe('ReactIncremental', () => {
|
||||
super(props);
|
||||
this.state = {x: this.props.x + '(ctor)'};
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
ops.push('componentWillMount:' + this.state.x);
|
||||
this.setState({x: this.props.x + '(willMount)'});
|
||||
}
|
||||
@@ -1334,13 +1334,13 @@ describe('ReactIncremental', () => {
|
||||
let ops = [];
|
||||
|
||||
class LifeCycle extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
ops.push('componentWillMount:' + this.props.x);
|
||||
}
|
||||
componentDidMount() {
|
||||
ops.push('componentDidMount:' + this.props.x);
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
ops.push(
|
||||
'componentWillReceiveProps:' + this.props.x + '-' + nextProps.x,
|
||||
);
|
||||
@@ -1349,7 +1349,7 @@ describe('ReactIncremental', () => {
|
||||
ops.push('shouldComponentUpdate:' + this.props.x + '-' + nextProps.x);
|
||||
return true;
|
||||
}
|
||||
componentWillUpdate(nextProps) {
|
||||
UNSAFE_componentWillUpdate(nextProps) {
|
||||
ops.push('componentWillUpdate:' + this.props.x + '-' + nextProps.x);
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
@@ -1417,6 +1417,44 @@ describe('ReactIncremental', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('does not call static getDerivedStateFromProps for state-only updates', () => {
|
||||
let ops = [];
|
||||
let instance;
|
||||
|
||||
class LifeCycle extends React.Component {
|
||||
state = {};
|
||||
static getDerivedStateFromProps(props, prevState) {
|
||||
ops.push('getDerivedStateFromProps');
|
||||
return {foo: 'foo'};
|
||||
}
|
||||
changeState() {
|
||||
this.setState({foo: 'bar'});
|
||||
}
|
||||
componentWillUpdate() {
|
||||
ops.push('componentWillUpdate');
|
||||
}
|
||||
render() {
|
||||
ops.push('render');
|
||||
instance = this;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
ReactNoop.render(<LifeCycle />);
|
||||
ReactNoop.flush();
|
||||
|
||||
expect(ops).toEqual(['getDerivedStateFromProps', 'render']);
|
||||
expect(instance.state).toEqual({foo: 'foo'});
|
||||
|
||||
ops = [];
|
||||
|
||||
instance.changeState();
|
||||
ReactNoop.flush();
|
||||
|
||||
expect(ops).toEqual(['componentWillUpdate', 'render']);
|
||||
expect(instance.state).toEqual({foo: 'bar'});
|
||||
});
|
||||
|
||||
xit('does not call componentWillReceiveProps for state-only updates', () => {
|
||||
let ops = [];
|
||||
|
||||
@@ -1429,21 +1467,21 @@ describe('ReactIncremental', () => {
|
||||
x: this.state.x + 1,
|
||||
});
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
instances.push(this);
|
||||
ops.push('componentWillMount:' + this.state.x);
|
||||
}
|
||||
componentDidMount() {
|
||||
ops.push('componentDidMount:' + this.state.x);
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
ops.push('componentWillReceiveProps');
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
ops.push('shouldComponentUpdate:' + this.state.x + '-' + nextState.x);
|
||||
return true;
|
||||
}
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
UNSAFE_componentWillUpdate(nextProps, nextState) {
|
||||
ops.push('componentWillUpdate:' + this.state.x + '-' + nextState.x);
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
@@ -1460,7 +1498,7 @@ describe('ReactIncremental', () => {
|
||||
// output unless it fully completed.
|
||||
class Wrap extends React.Component {
|
||||
state = {y: 0};
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
instances.push(this);
|
||||
}
|
||||
tick() {
|
||||
@@ -1570,13 +1608,13 @@ describe('ReactIncremental', () => {
|
||||
let ops = [];
|
||||
|
||||
class LifeCycle extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
ops.push('componentWillMount');
|
||||
}
|
||||
componentDidMount() {
|
||||
ops.push('componentDidMount');
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
ops.push('componentWillReceiveProps');
|
||||
}
|
||||
shouldComponentUpdate(nextProps) {
|
||||
@@ -1584,7 +1622,7 @@ describe('ReactIncremental', () => {
|
||||
// Bail
|
||||
return this.props.x !== nextProps.x;
|
||||
}
|
||||
componentWillUpdate(nextProps) {
|
||||
UNSAFE_componentWillUpdate(nextProps) {
|
||||
ops.push('componentWillUpdate');
|
||||
}
|
||||
componentDidUpdate(prevProps) {
|
||||
@@ -2282,7 +2320,7 @@ describe('ReactIncremental', () => {
|
||||
this.setState({setStateInCDU: false});
|
||||
}
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
ops.push('componentWillReceiveProps');
|
||||
this.setState({setStateInCDU: true});
|
||||
}
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ describe('ReactIncrementalErrorLogging', () => {
|
||||
spyOnProd(console, 'error');
|
||||
|
||||
class ErrorThrowingComponent extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
const error = new Error('componentWillMount error');
|
||||
// Note: it's `true` on the Error prototype our test environment.
|
||||
// That lets us avoid asserting on warnings for each expected error.
|
||||
|
||||
@@ -237,10 +237,10 @@ describe('ReactDebugFiberPerf', () => {
|
||||
|
||||
it('does not treat setState from cWM or cWRP as cascading', () => {
|
||||
class NotCascading extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({});
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
this.setState({});
|
||||
}
|
||||
render() {
|
||||
@@ -276,10 +276,10 @@ describe('ReactDebugFiberPerf', () => {
|
||||
getChildContext() {
|
||||
return {foo: 42};
|
||||
}
|
||||
componentWillMount() {}
|
||||
UNSAFE_componentWillMount() {}
|
||||
componentDidMount() {}
|
||||
componentWillReceiveProps() {}
|
||||
componentWillUpdate() {}
|
||||
UNSAFE_componentWillReceiveProps() {}
|
||||
UNSAFE_componentWillUpdate() {}
|
||||
componentDidUpdate() {}
|
||||
componentWillUnmount() {}
|
||||
render() {
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('ReactIncrementalReflection', () => {
|
||||
// reaching into the updater.
|
||||
return this.updater.isMounted(this);
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
instances.push(this);
|
||||
ops.push('componentWillMount', this._isMounted());
|
||||
}
|
||||
@@ -75,7 +75,7 @@ describe('ReactIncrementalReflection', () => {
|
||||
_isMounted() {
|
||||
return this.updater.isMounted(this);
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
instances.push(this);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
@@ -128,14 +128,14 @@ describe('ReactIncrementalReflection', () => {
|
||||
let classInstance = null;
|
||||
|
||||
class Component extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
classInstance = this;
|
||||
ops.push('componentWillMount', ReactNoop.findInstance(this));
|
||||
}
|
||||
componentDidMount() {
|
||||
ops.push('componentDidMount', ReactNoop.findInstance(this));
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
ops.push('componentWillUpdate', ReactNoop.findInstance(this));
|
||||
}
|
||||
componentDidUpdate() {
|
||||
|
||||
@@ -331,7 +331,7 @@ describe('ReactIncrementalScheduling', () => {
|
||||
it('updates do not schedule a new callback if already inside a callback', () => {
|
||||
class Foo extends React.Component {
|
||||
state = {foo: 'foo'};
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
ReactNoop.yield(
|
||||
'has callback before setState: ' + ReactNoop.hasScheduledCallback(),
|
||||
);
|
||||
|
||||
@@ -296,7 +296,7 @@ describe('ReactIncrementalUpdates', () => {
|
||||
|
||||
class Foo extends React.Component {
|
||||
state = {};
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
ops.push('componentWillReceiveProps');
|
||||
this.setState({b: 'b'});
|
||||
}
|
||||
|
||||
+186
-12
@@ -7,12 +7,32 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {warnAboutDeprecatedLifecycles} from 'shared/ReactFeatureFlags';
|
||||
import describeComponentFrame from 'shared/describeComponentFrame';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import emptyObject from 'fbjs/lib/emptyObject';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import shallowEqual from 'fbjs/lib/shallowEqual';
|
||||
import checkPropTypes from 'prop-types/checkPropTypes';
|
||||
import warning from 'fbjs/lib/warning';
|
||||
|
||||
let didWarnAboutLegacyWillMount;
|
||||
let didWarnAboutLegacyWillReceiveProps;
|
||||
let didWarnAboutLegacyWillUpdate;
|
||||
let didWarnAboutUndefinedDerivedState;
|
||||
let didWarnAboutUninitializedState;
|
||||
let didWarnAboutWillReceivePropsAndDerivedState;
|
||||
|
||||
if (__DEV__) {
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
didWarnAboutLegacyWillMount = {};
|
||||
didWarnAboutLegacyWillReceiveProps = {};
|
||||
didWarnAboutLegacyWillUpdate = {};
|
||||
}
|
||||
didWarnAboutUndefinedDerivedState = {};
|
||||
didWarnAboutUninitializedState = {};
|
||||
didWarnAboutWillReceivePropsAndDerivedState = {};
|
||||
}
|
||||
|
||||
class ReactShallowRenderer {
|
||||
static createRenderer = function() {
|
||||
@@ -73,7 +93,7 @@ class ReactShallowRenderer {
|
||||
this._context = getMaskedContext(element.type.contextTypes, context);
|
||||
|
||||
if (this._instance) {
|
||||
this._updateClassComponent(element.type, element.props, this._context);
|
||||
this._updateClassComponent(element, this._context);
|
||||
} else {
|
||||
if (shouldConstruct(element.type)) {
|
||||
this._instance = new element.type(
|
||||
@@ -82,6 +102,30 @@ class ReactShallowRenderer {
|
||||
this._updater,
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
if (typeof element.type.getDerivedStateFromProps === 'function') {
|
||||
if (
|
||||
this._instance.state === null ||
|
||||
this._instance.state === undefined
|
||||
) {
|
||||
const componentName =
|
||||
getName(element.type, this._instance) || 'Unknown';
|
||||
if (!didWarnAboutUninitializedState[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: Did not properly initialize state during construction. ' +
|
||||
'Expected state to be an object, but it was %s.',
|
||||
componentName,
|
||||
this._instance.state === null ? 'null' : 'undefined',
|
||||
);
|
||||
didWarnAboutUninitializedState[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._updateStateFromStaticLifecycle(element.props);
|
||||
|
||||
if (element.type.hasOwnProperty('contextTypes')) {
|
||||
currentlyValidatingElement = element;
|
||||
|
||||
@@ -96,7 +140,7 @@ class ReactShallowRenderer {
|
||||
currentlyValidatingElement = null;
|
||||
}
|
||||
|
||||
this._mountClassComponent(element.props, this._context);
|
||||
this._mountClassComponent(element, this._context);
|
||||
} else {
|
||||
this._rendered = element.type(element.props, this._context);
|
||||
}
|
||||
@@ -122,16 +166,42 @@ class ReactShallowRenderer {
|
||||
this._instance = null;
|
||||
}
|
||||
|
||||
_mountClassComponent(props, context) {
|
||||
_mountClassComponent(element, context) {
|
||||
this._instance.context = context;
|
||||
this._instance.props = props;
|
||||
this._instance.props = element.props;
|
||||
this._instance.state = this._instance.state || null;
|
||||
this._instance.updater = this._updater;
|
||||
|
||||
if (typeof this._instance.componentWillMount === 'function') {
|
||||
if (
|
||||
typeof this._instance.UNSAFE_componentWillMount === 'function' ||
|
||||
typeof this._instance.componentWillMount === 'function'
|
||||
) {
|
||||
const beforeState = this._newState;
|
||||
|
||||
this._instance.componentWillMount();
|
||||
if (typeof this._instance.componentWillMount === 'function') {
|
||||
if (__DEV__) {
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
const componentName = getName(element.type, this._instance);
|
||||
if (!didWarnAboutLegacyWillMount[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: componentWillMount() is deprecated and will be ' +
|
||||
'removed in the next major version. Read about the motivations ' +
|
||||
'behind this change: ' +
|
||||
'https://fb.me/react-async-component-lifecycle-hooks' +
|
||||
'\n\n' +
|
||||
'As a temporary workaround, you can rename to ' +
|
||||
'UNSAFE_componentWillMount instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutLegacyWillMount[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._instance.componentWillMount();
|
||||
} else {
|
||||
this._instance.UNSAFE_componentWillMount();
|
||||
}
|
||||
|
||||
// setState may have been called during cWM
|
||||
if (beforeState !== this._newState) {
|
||||
@@ -144,16 +214,44 @@ class ReactShallowRenderer {
|
||||
// because DOM refs are not available.
|
||||
}
|
||||
|
||||
_updateClassComponent(type, props, context) {
|
||||
_updateClassComponent(element, context) {
|
||||
const {props, type} = element;
|
||||
|
||||
const oldState = this._instance.state || emptyObject;
|
||||
const oldProps = this._instance.props;
|
||||
|
||||
if (
|
||||
oldProps !== props &&
|
||||
typeof this._instance.componentWillReceiveProps === 'function'
|
||||
) {
|
||||
this._instance.componentWillReceiveProps(props, context);
|
||||
if (oldProps !== props) {
|
||||
if (typeof this._instance.componentWillReceiveProps === 'function') {
|
||||
if (__DEV__) {
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
const componentName = getName(element.type, this._instance);
|
||||
if (!didWarnAboutLegacyWillReceiveProps[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: componentWillReceiveProps() is deprecated and ' +
|
||||
'will be removed in the next major version. Use ' +
|
||||
'static getDerivedStateFromProps() instead. Read about the ' +
|
||||
'motivations behind this change: ' +
|
||||
'https://fb.me/react-async-component-lifecycle-hooks' +
|
||||
'\n\n' +
|
||||
'As a temporary workaround, you can rename to ' +
|
||||
'UNSAFE_componentWillReceiveProps instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutLegacyWillReceiveProps[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
this._instance.componentWillReceiveProps(props, context);
|
||||
} else if (
|
||||
typeof this._instance.UNSAFE_componentWillReceiveProps === 'function'
|
||||
) {
|
||||
this._instance.UNSAFE_componentWillReceiveProps(props, context);
|
||||
}
|
||||
|
||||
this._updateStateFromStaticLifecycle(props);
|
||||
}
|
||||
|
||||
// Read state after cWRP in case it calls setState
|
||||
const state = this._newState || oldState;
|
||||
|
||||
@@ -174,7 +272,31 @@ class ReactShallowRenderer {
|
||||
|
||||
if (shouldUpdate) {
|
||||
if (typeof this._instance.componentWillUpdate === 'function') {
|
||||
if (__DEV__) {
|
||||
if (warnAboutDeprecatedLifecycles) {
|
||||
const componentName = getName(element.type, this._instance);
|
||||
if (!didWarnAboutLegacyWillUpdate[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: componentWillUpdate() is deprecated and will be ' +
|
||||
'removed in the next major version. Read about the motivations ' +
|
||||
'behind this change: ' +
|
||||
'https://fb.me/react-async-component-lifecycle-hooks' +
|
||||
'\n\n' +
|
||||
'As a temporary workaround, you can rename to ' +
|
||||
'UNSAFE_componentWillUpdate instead.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutLegacyWillUpdate[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this._instance.componentWillUpdate(props, state, context);
|
||||
} else if (
|
||||
typeof this._instance.UNSAFE_componentWillUpdate === 'function'
|
||||
) {
|
||||
this._instance.UNSAFE_componentWillUpdate(props, state, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +310,58 @@ class ReactShallowRenderer {
|
||||
// Intentionally do not call componentDidUpdate()
|
||||
// because DOM refs are not available.
|
||||
}
|
||||
|
||||
_updateStateFromStaticLifecycle(props) {
|
||||
const {type} = this._element;
|
||||
|
||||
if (typeof type.getDerivedStateFromProps === 'function') {
|
||||
if (__DEV__) {
|
||||
if (
|
||||
typeof this._instance.componentWillReceiveProps === 'function' ||
|
||||
typeof this._instance.UNSAFE_componentWillReceiveProps === 'function'
|
||||
) {
|
||||
const componentName = getName(type, this._instance);
|
||||
if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s: Defines both componentWillReceiveProps() and static ' +
|
||||
'getDerivedStateFromProps() methods. We recommend using ' +
|
||||
'only getDerivedStateFromProps().',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const partialState = type.getDerivedStateFromProps.call(
|
||||
null,
|
||||
props,
|
||||
this._instance.state,
|
||||
);
|
||||
|
||||
if (__DEV__) {
|
||||
if (partialState === undefined) {
|
||||
const componentName = getName(type, this._instance);
|
||||
if (!didWarnAboutUndefinedDerivedState[componentName]) {
|
||||
warning(
|
||||
false,
|
||||
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
|
||||
'You have returned undefined.',
|
||||
componentName,
|
||||
);
|
||||
didWarnAboutUndefinedDerivedState[componentName] = componentName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (partialState != null) {
|
||||
const oldState = this._newState || this._instance.state;
|
||||
const newState = Object.assign({}, oldState, partialState);
|
||||
this._instance.state = this._newState = newState;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Updater {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
* @jest-environment node
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let createRenderer;
|
||||
let React;
|
||||
let ReactFeatureFlags;
|
||||
|
||||
describe('ReactShallowRenderer', () => {
|
||||
beforeEach(() => {
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.warnAboutDeprecatedLifecycles = true;
|
||||
|
||||
createRenderer = require('react-test-renderer/shallow').createRenderer;
|
||||
React = require('react');
|
||||
});
|
||||
|
||||
// TODO (RFC #6) Merge this back into ReactShallowRenderer-test once
|
||||
// the 'warnAboutDeprecatedLifecycles' feature flag has been removed.
|
||||
it('should warn if deprecated lifecycles exist', () => {
|
||||
class ComponentWithWarnings extends React.Component {
|
||||
componentWillReceiveProps() {}
|
||||
componentWillMount() {}
|
||||
componentWillUpdate() {}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const shallowRenderer = createRenderer();
|
||||
expect(() => shallowRenderer.render(<ComponentWithWarnings />)).toWarnDev(
|
||||
'Warning: ComponentWithWarnings: componentWillMount() is deprecated and will ' +
|
||||
'be removed in the next major version.',
|
||||
);
|
||||
expect(() => shallowRenderer.render(<ComponentWithWarnings />)).toWarnDev([
|
||||
'Warning: ComponentWithWarnings: componentWillReceiveProps() is deprecated ' +
|
||||
'and will be removed in the next major version.',
|
||||
'Warning: ComponentWithWarnings: componentWillUpdate() is deprecated and will ' +
|
||||
'be removed in the next major version.',
|
||||
]);
|
||||
|
||||
// Verify no duplicate warnings
|
||||
shallowRenderer.render(<ComponentWithWarnings />);
|
||||
});
|
||||
});
|
||||
@@ -16,6 +16,8 @@ let React;
|
||||
|
||||
describe('ReactShallowRenderer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
createRenderer = require('react-test-renderer/shallow').createRenderer;
|
||||
PropTypes = require('prop-types');
|
||||
React = require('react');
|
||||
@@ -26,11 +28,13 @@ describe('ReactShallowRenderer', () => {
|
||||
const logger = message => () => logs.push(message) || true;
|
||||
|
||||
class SomeComponent extends React.Component {
|
||||
componentWillMount = logger('componentWillMount');
|
||||
state = {};
|
||||
static getDerivedStateFromProps = logger('getDerivedStateFromProps');
|
||||
UNSAFE_componentWillMount = logger('componentWillMount');
|
||||
componentDidMount = logger('componentDidMount');
|
||||
componentWillReceiveProps = logger('componentWillReceiveProps');
|
||||
UNSAFE_componentWillReceiveProps = logger('componentWillReceiveProps');
|
||||
shouldComponentUpdate = logger('shouldComponentUpdate');
|
||||
componentWillUpdate = logger('componentWillUpdate');
|
||||
UNSAFE_componentWillUpdate = logger('componentWillUpdate');
|
||||
componentDidUpdate = logger('componentDidUpdate');
|
||||
componentWillUnmount = logger('componentWillUnmount');
|
||||
render() {
|
||||
@@ -39,11 +43,16 @@ describe('ReactShallowRenderer', () => {
|
||||
}
|
||||
|
||||
const shallowRenderer = createRenderer();
|
||||
shallowRenderer.render(<SomeComponent foo={1} />);
|
||||
|
||||
expect(() => shallowRenderer.render(<SomeComponent foo={1} />)).toWarnDev(
|
||||
'Warning: SomeComponent: Defines both componentWillReceiveProps() and static ' +
|
||||
'getDerivedStateFromProps() methods. ' +
|
||||
'We recommend using only getDerivedStateFromProps().',
|
||||
);
|
||||
|
||||
// Calling cDU might lead to problems with host component references.
|
||||
// Since our components aren't really mounted, refs won't be available.
|
||||
expect(logs).toEqual(['componentWillMount']);
|
||||
expect(logs).toEqual(['getDerivedStateFromProps', 'componentWillMount']);
|
||||
|
||||
logs.splice(0);
|
||||
|
||||
@@ -59,6 +68,7 @@ describe('ReactShallowRenderer', () => {
|
||||
// The previous shallow renderer did not trigger cDU for props changes.
|
||||
expect(logs).toEqual([
|
||||
'componentWillReceiveProps',
|
||||
'getDerivedStateFromProps',
|
||||
'shouldComponentUpdate',
|
||||
'componentWillUpdate',
|
||||
]);
|
||||
@@ -416,6 +426,7 @@ describe('ReactShallowRenderer', () => {
|
||||
const componentDidUpdateParams = [];
|
||||
const componentWillReceivePropsParams = [];
|
||||
const componentWillUpdateParams = [];
|
||||
const getDerivedStateFromPropsParams = [];
|
||||
const setStateParams = [];
|
||||
const shouldComponentUpdateParams = [];
|
||||
|
||||
@@ -437,14 +448,18 @@ describe('ReactShallowRenderer', () => {
|
||||
componentDidUpdate(...args) {
|
||||
componentDidUpdateParams.push(...args);
|
||||
}
|
||||
componentWillReceiveProps(...args) {
|
||||
static getDerivedStateFromProps(...args) {
|
||||
getDerivedStateFromPropsParams.push(args);
|
||||
return null;
|
||||
}
|
||||
UNSAFE_componentWillReceiveProps(...args) {
|
||||
componentWillReceivePropsParams.push(...args);
|
||||
this.setState((...innerArgs) => {
|
||||
setStateParams.push(...innerArgs);
|
||||
return updatedState;
|
||||
});
|
||||
}
|
||||
componentWillUpdate(...args) {
|
||||
UNSAFE_componentWillUpdate(...args) {
|
||||
componentWillUpdateParams.push(...args);
|
||||
}
|
||||
shouldComponentUpdate(...args) {
|
||||
@@ -458,11 +473,21 @@ describe('ReactShallowRenderer', () => {
|
||||
|
||||
const shallowRenderer = createRenderer();
|
||||
|
||||
// No lifecycle hooks should be invoked on initial render
|
||||
shallowRenderer.render(
|
||||
React.createElement(SimpleComponent, initialProp),
|
||||
initialContext,
|
||||
// The only lifecycle hook that should be invoked on initial render
|
||||
// Is the static getDerivedStateFromProps() methods
|
||||
expect(() =>
|
||||
shallowRenderer.render(
|
||||
React.createElement(SimpleComponent, initialProp),
|
||||
initialContext,
|
||||
),
|
||||
).toWarnDev(
|
||||
'SimpleComponent: Defines both componentWillReceiveProps() and static ' +
|
||||
'getDerivedStateFromProps() methods. We recommend using ' +
|
||||
'only getDerivedStateFromProps().',
|
||||
);
|
||||
expect(getDerivedStateFromPropsParams).toEqual([
|
||||
[initialProp, initialState],
|
||||
]);
|
||||
expect(componentDidUpdateParams).toEqual([]);
|
||||
expect(componentWillReceivePropsParams).toEqual([]);
|
||||
expect(componentWillUpdateParams).toEqual([]);
|
||||
@@ -479,6 +504,10 @@ describe('ReactShallowRenderer', () => {
|
||||
updatedContext,
|
||||
]);
|
||||
expect(setStateParams).toEqual([initialState, initialProp]);
|
||||
expect(getDerivedStateFromPropsParams).toEqual([
|
||||
[initialProp, initialState],
|
||||
[updatedProp, initialState],
|
||||
]);
|
||||
expect(shouldComponentUpdateParams).toEqual([
|
||||
updatedProp,
|
||||
updatedState,
|
||||
@@ -523,9 +552,34 @@ describe('ReactShallowRenderer', () => {
|
||||
expect(result.props.className).toEqual('clicked');
|
||||
});
|
||||
|
||||
it('can initialize state via static getDerivedStateFromProps', () => {
|
||||
class SimpleComponent extends React.Component {
|
||||
state = {
|
||||
count: 1,
|
||||
};
|
||||
|
||||
static getDerivedStateFromProps(props, prevState) {
|
||||
return {
|
||||
count: prevState.count + props.incrementBy,
|
||||
other: 'foobar',
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>{`count:${this.state.count}, other:${this.state.other}`}</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const shallowRenderer = createRenderer();
|
||||
const result = shallowRenderer.render(<SimpleComponent incrementBy={2} />);
|
||||
expect(result).toEqual(<div>count:3, other:foobar</div>);
|
||||
});
|
||||
|
||||
it('can setState in componentWillMount when shallow rendering', () => {
|
||||
class SimpleComponent extends React.Component {
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({groovy: 'doovy'});
|
||||
}
|
||||
|
||||
@@ -545,7 +599,7 @@ describe('ReactShallowRenderer', () => {
|
||||
separator: '-',
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({groovy: 'doovy'});
|
||||
this.setState({doovy: 'groovy'});
|
||||
}
|
||||
@@ -568,7 +622,7 @@ describe('ReactShallowRenderer', () => {
|
||||
separator: '-',
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState(state => ({groovy: 'doovy'}));
|
||||
this.setState(state => ({doovy: state.groovy}));
|
||||
}
|
||||
@@ -589,7 +643,7 @@ describe('ReactShallowRenderer', () => {
|
||||
class SimpleComponent extends React.Component {
|
||||
state = {count: 0};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.updateState) {
|
||||
this.setState({count: 1});
|
||||
}
|
||||
@@ -610,6 +664,40 @@ describe('ReactShallowRenderer', () => {
|
||||
expect(result.props.children).toEqual(1);
|
||||
});
|
||||
|
||||
it('can update state with static getDerivedStateFromProps when shallow rendering', () => {
|
||||
class SimpleComponent extends React.Component {
|
||||
state = {count: 1};
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.updateState) {
|
||||
return {count: nextProps.incrementBy + prevState.count};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div>{this.state.count}</div>;
|
||||
}
|
||||
}
|
||||
|
||||
const shallowRenderer = createRenderer();
|
||||
let result = shallowRenderer.render(
|
||||
<SimpleComponent updateState={false} incrementBy={0} />,
|
||||
);
|
||||
expect(result.props.children).toEqual(1);
|
||||
|
||||
result = shallowRenderer.render(
|
||||
<SimpleComponent updateState={true} incrementBy={2} />,
|
||||
);
|
||||
expect(result.props.children).toEqual(3);
|
||||
|
||||
result = shallowRenderer.render(
|
||||
<SimpleComponent updateState={false} incrementBy={2} />,
|
||||
);
|
||||
expect(result.props.children).toEqual(3);
|
||||
});
|
||||
|
||||
it('can setState with an updater function', () => {
|
||||
let instance;
|
||||
|
||||
@@ -870,7 +958,7 @@ describe('ReactShallowRenderer', () => {
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState(
|
||||
{hasUpdatedState: true},
|
||||
() => (stateSuccessfullyUpdated = this.state.hasUpdatedState),
|
||||
@@ -899,7 +987,7 @@ describe('ReactShallowRenderer', () => {
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({foo: 'bar'}, () => mockFn());
|
||||
this.setState({foo: 'foobar'}, () => mockFn());
|
||||
}
|
||||
@@ -984,4 +1072,66 @@ describe('ReactShallowRenderer', () => {
|
||||
|
||||
expect(shallowRenderer.getMountedInstance().state).toBeNull();
|
||||
});
|
||||
|
||||
it('should warn if both componentWillReceiveProps and static getDerivedStateFromProps exist', () => {
|
||||
class ComponentWithWarnings extends React.Component {
|
||||
state = {};
|
||||
static getDerivedStateFromProps(props, prevState) {
|
||||
return null;
|
||||
}
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const shallowRenderer = createRenderer();
|
||||
expect(() => shallowRenderer.render(<ComponentWithWarnings />)).toWarnDev(
|
||||
'ComponentWithWarnings: Defines both componentWillReceiveProps() and static ' +
|
||||
'getDerivedStateFromProps() methods. We recommend using ' +
|
||||
'only getDerivedStateFromProps().',
|
||||
);
|
||||
|
||||
// Should not log duplicate warning
|
||||
shallowRenderer.render(<ComponentWithWarnings />);
|
||||
});
|
||||
|
||||
it('should warn if getDerivedStateFromProps returns undefined', () => {
|
||||
class Component extends React.Component {
|
||||
state = {};
|
||||
static getDerivedStateFromProps() {}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const shallowRenderer = createRenderer();
|
||||
expect(() => shallowRenderer.render(<Component />)).toWarnDev(
|
||||
'Component.getDerivedStateFromProps(): A valid state object (or null) must ' +
|
||||
'be returned. You have returned undefined.',
|
||||
);
|
||||
|
||||
// De-duped
|
||||
shallowRenderer.render(<Component />);
|
||||
});
|
||||
|
||||
it('should warn if state not initialized before getDerivedStateFromProps', () => {
|
||||
class Component extends React.Component {
|
||||
static getDerivedStateFromProps() {
|
||||
return null;
|
||||
}
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const shallowRenderer = createRenderer();
|
||||
expect(() => shallowRenderer.render(<Component />)).toWarnDev(
|
||||
'Component: Did not properly initialize state during construction. ' +
|
||||
'Expected state to be an object, but it was undefined.',
|
||||
);
|
||||
|
||||
// De-duped
|
||||
shallowRenderer.render(<Component />);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,16 +38,16 @@ describe('ReactAsyncClassComponent', () => {
|
||||
componentDidUpdate() {
|
||||
log.push('componentDidUpdate');
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
log.push('componentWillMount');
|
||||
}
|
||||
componentWillReceiveProps() {
|
||||
UNSAFE_componentWillReceiveProps() {
|
||||
log.push('componentWillReceiveProps');
|
||||
}
|
||||
componentWillUnmount() {
|
||||
log.push('componentWillUnmount');
|
||||
}
|
||||
componentWillUpdate() {
|
||||
UNSAFE_componentWillUpdate() {
|
||||
log.push('componentWillUpdate');
|
||||
}
|
||||
shouldComponentUpdate() {
|
||||
|
||||
@@ -98,6 +98,73 @@ describe 'ReactCoffeeScriptClass', ->
|
||||
test React.createElement(Foo), 'SPAN', 'bar'
|
||||
undefined
|
||||
|
||||
it 'sets initial state with value returned by static getDerivedStateFromProps', ->
|
||||
class Foo extends React.Component
|
||||
constructor: (props) ->
|
||||
super props
|
||||
@state = foo: null
|
||||
render: ->
|
||||
div
|
||||
className: "#{@state.foo} #{@state.bar}"
|
||||
Foo.getDerivedStateFromProps = (nextProps, prevState) ->
|
||||
{
|
||||
foo: nextProps.foo
|
||||
bar: 'bar'
|
||||
}
|
||||
test React.createElement(Foo, foo: 'foo'), 'DIV', 'foo bar'
|
||||
undefined
|
||||
|
||||
it 'warns if state not initialized before static getDerivedStateFromProps', ->
|
||||
class Foo extends React.Component
|
||||
render: ->
|
||||
div
|
||||
className: "#{@state.foo} #{@state.bar}"
|
||||
Foo.getDerivedStateFromProps = (nextProps, prevState) ->
|
||||
{
|
||||
foo: nextProps.foo
|
||||
bar: 'bar'
|
||||
}
|
||||
expect(->
|
||||
ReactDOM.render(React.createElement(Foo, foo: 'foo'), container)
|
||||
).toWarnDev 'Foo: Did not properly initialize state during construction. Expected state to be an object, but it was undefined.'
|
||||
undefined
|
||||
|
||||
it 'updates initial state with values returned by static getDerivedStateFromProps', ->
|
||||
class Foo extends React.Component
|
||||
constructor: (props, context) ->
|
||||
super props, context
|
||||
@state =
|
||||
foo: 'foo'
|
||||
bar: 'bar'
|
||||
render: ->
|
||||
div
|
||||
className: "#{@state.foo} #{@state.bar}"
|
||||
Foo.getDerivedStateFromProps = (nextProps, prevState) ->
|
||||
{
|
||||
foo: "not-#{prevState.foo}"
|
||||
}
|
||||
test React.createElement(Foo), 'DIV', 'not-foo bar'
|
||||
undefined
|
||||
|
||||
it 'renders updated state with values returned by static getDerivedStateFromProps', ->
|
||||
class Foo extends React.Component
|
||||
constructor: (props, context) ->
|
||||
super props, context
|
||||
@state =
|
||||
value: 'initial'
|
||||
render: ->
|
||||
div
|
||||
className: @state.value
|
||||
Foo.getDerivedStateFromProps = (nextProps, prevState) ->
|
||||
if nextProps.update
|
||||
return {
|
||||
value: 'updated'
|
||||
}
|
||||
return null
|
||||
test React.createElement(Foo, update: false), 'DIV', 'initial'
|
||||
test React.createElement(Foo, update: true), 'DIV', 'updated'
|
||||
undefined
|
||||
|
||||
it 'renders based on context in the constructor', ->
|
||||
class Foo extends React.Component
|
||||
@contextTypes:
|
||||
@@ -136,7 +203,7 @@ describe 'ReactCoffeeScriptClass', ->
|
||||
constructor: (props) ->
|
||||
@state = bar: props.initialValue
|
||||
|
||||
componentWillMount: ->
|
||||
UNSAFE_componentWillMount: ->
|
||||
@setState bar: 'bar'
|
||||
|
||||
render: ->
|
||||
@@ -232,20 +299,20 @@ describe 'ReactCoffeeScriptClass', ->
|
||||
constructor: ->
|
||||
@state = {}
|
||||
|
||||
componentWillMount: ->
|
||||
UNSAFE_componentWillMount: ->
|
||||
lifeCycles.push 'will-mount'
|
||||
|
||||
componentDidMount: ->
|
||||
lifeCycles.push 'did-mount'
|
||||
|
||||
componentWillReceiveProps: (nextProps) ->
|
||||
UNSAFE_componentWillReceiveProps: (nextProps) ->
|
||||
lifeCycles.push 'receive-props', nextProps
|
||||
|
||||
shouldComponentUpdate: (nextProps, nextState) ->
|
||||
lifeCycles.push 'should-update', nextProps, nextState
|
||||
true
|
||||
|
||||
componentWillUpdate: (nextProps, nextState) ->
|
||||
UNSAFE_componentWillUpdate: (nextProps, nextState) ->
|
||||
lifeCycles.push 'will-update', nextProps, nextState
|
||||
|
||||
componentDidUpdate: (prevProps, prevState) ->
|
||||
@@ -361,6 +428,23 @@ describe 'ReactCoffeeScriptClass', ->
|
||||
)
|
||||
undefined
|
||||
|
||||
it 'should warn when misspelling UNSAFE_componentWillReceiveProps', ->
|
||||
class NamedComponent extends React.Component
|
||||
UNSAFE_componentWillRecieveProps: ->
|
||||
false
|
||||
|
||||
render: ->
|
||||
span
|
||||
className: 'foo'
|
||||
|
||||
expect(->
|
||||
test React.createElement(NamedComponent), 'SPAN', 'foo'
|
||||
).toWarnDev(
|
||||
'Warning: NamedComponent has a method called UNSAFE_componentWillRecieveProps().
|
||||
Did you mean UNSAFE_componentWillReceiveProps()?'
|
||||
)
|
||||
undefined
|
||||
|
||||
it 'should throw AND warn when trying to access classic APIs', ->
|
||||
instance =
|
||||
test Inner(name: 'foo'), 'DIV', 'foo'
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('ReactContextValidator', () => {
|
||||
};
|
||||
|
||||
class Component extends React.Component {
|
||||
componentWillReceiveProps(nextProps, nextContext) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps, nextContext) {
|
||||
actualComponentWillReceiveProps = nextContext;
|
||||
return true;
|
||||
}
|
||||
@@ -99,7 +99,7 @@ describe('ReactContextValidator', () => {
|
||||
return true;
|
||||
}
|
||||
|
||||
componentWillUpdate(nextProps, nextState, nextContext) {
|
||||
UNSAFE_componentWillUpdate(nextProps, nextState, nextContext) {
|
||||
actualComponentWillUpdate = nextContext;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,6 +109,79 @@ describe('ReactES6Class', () => {
|
||||
test(<Foo />, 'SPAN', 'bar');
|
||||
});
|
||||
|
||||
it('sets initial state with value returned by static getDerivedStateFromProps', () => {
|
||||
class Foo extends React.Component {
|
||||
state = {};
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
return {
|
||||
foo: nextProps.foo,
|
||||
bar: 'bar',
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return <div className={`${this.state.foo} ${this.state.bar}`} />;
|
||||
}
|
||||
}
|
||||
test(<Foo foo="foo" />, 'DIV', 'foo bar');
|
||||
});
|
||||
|
||||
it('warns if state not initialized before static getDerivedStateFromProps', () => {
|
||||
class Foo extends React.Component {
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
return {
|
||||
foo: nextProps.foo,
|
||||
bar: 'bar',
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return <div className={`${this.state.foo} ${this.state.bar}`} />;
|
||||
}
|
||||
}
|
||||
expect(() => ReactDOM.render(<Foo foo="foo" />, container)).toWarnDev(
|
||||
'Foo: Did not properly initialize state during construction. ' +
|
||||
'Expected state to be an object, but it was undefined.',
|
||||
);
|
||||
});
|
||||
|
||||
it('updates initial state with values returned by static getDerivedStateFromProps', () => {
|
||||
class Foo extends React.Component {
|
||||
state = {
|
||||
foo: 'foo',
|
||||
bar: 'bar',
|
||||
};
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
return {
|
||||
foo: `not-${prevState.foo}`,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return <div className={`${this.state.foo} ${this.state.bar}`} />;
|
||||
}
|
||||
}
|
||||
test(<Foo />, 'DIV', 'not-foo bar');
|
||||
});
|
||||
|
||||
it('renders updated state with values returned by static getDerivedStateFromProps', () => {
|
||||
class Foo extends React.Component {
|
||||
state = {
|
||||
value: 'initial',
|
||||
};
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.update) {
|
||||
return {
|
||||
value: 'updated',
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
render() {
|
||||
return <div className={this.state.value} />;
|
||||
}
|
||||
}
|
||||
test(<Foo update={false} />, 'DIV', 'initial');
|
||||
test(<Foo update={true} />, 'DIV', 'updated');
|
||||
});
|
||||
|
||||
it('renders based on context in the constructor', () => {
|
||||
class Foo extends React.Component {
|
||||
constructor(props, context) {
|
||||
@@ -147,7 +220,7 @@ describe('ReactES6Class', () => {
|
||||
super(props);
|
||||
this.state = {bar: props.initialValue};
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({bar: 'bar'});
|
||||
}
|
||||
render() {
|
||||
@@ -257,20 +330,20 @@ describe('ReactES6Class', () => {
|
||||
super();
|
||||
this.state = {};
|
||||
}
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
lifeCycles.push('will-mount');
|
||||
}
|
||||
componentDidMount() {
|
||||
lifeCycles.push('did-mount');
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
lifeCycles.push('receive-props', nextProps);
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
lifeCycles.push('should-update', nextProps, nextState);
|
||||
return true;
|
||||
}
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
UNSAFE_componentWillUpdate(nextProps, nextState) {
|
||||
lifeCycles.push('will-update', nextProps, nextState);
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
@@ -379,6 +452,23 @@ describe('ReactES6Class', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn when misspelling UNSAFE_componentWillReceiveProps', () => {
|
||||
class NamedComponent extends React.Component {
|
||||
UNSAFE_componentWillRecieveProps() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
return <span className="foo" />;
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => test(<NamedComponent />, 'SPAN', 'foo')).toWarnDev(
|
||||
'Warning: ' +
|
||||
'NamedComponent has a method called UNSAFE_componentWillRecieveProps(). ' +
|
||||
'Did you mean UNSAFE_componentWillReceiveProps()?',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw AND warn when trying to access classic APIs', () => {
|
||||
const instance = test(<Inner name="foo" />, 'DIV', 'foo');
|
||||
expect(() =>
|
||||
|
||||
@@ -117,7 +117,7 @@ class RenderOnce extends React.Component {
|
||||
state = {
|
||||
bar: this.props.initialValue,
|
||||
};
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
this.setState({bar: 'bar'});
|
||||
}
|
||||
render() {
|
||||
@@ -206,20 +206,20 @@ let lifeCycles = [];
|
||||
class NormalLifeCycles extends React.Component {
|
||||
props: any;
|
||||
state = {};
|
||||
componentWillMount() {
|
||||
UNSAFE_componentWillMount() {
|
||||
lifeCycles.push('will-mount');
|
||||
}
|
||||
componentDidMount() {
|
||||
lifeCycles.push('did-mount');
|
||||
}
|
||||
componentWillReceiveProps(nextProps) {
|
||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
||||
lifeCycles.push('receive-props', nextProps);
|
||||
}
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
lifeCycles.push('should-update', nextProps, nextState);
|
||||
return true;
|
||||
}
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
UNSAFE_componentWillUpdate(nextProps, nextState) {
|
||||
lifeCycles.push('will-update', nextProps, nextState);
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
@@ -273,6 +273,16 @@ class MisspelledComponent2 extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
// it should warn when misspelling UNSAFE_componentWillReceiveProps
|
||||
class MisspelledComponent3 extends React.Component {
|
||||
UNSAFE_componentWillRecieveProps() {
|
||||
return false;
|
||||
}
|
||||
render() {
|
||||
return React.createElement('span', {className: 'foo'});
|
||||
}
|
||||
}
|
||||
|
||||
// it supports this.context passed via getChildContext
|
||||
class ReadContext extends React.Component {
|
||||
static contextTypes = {bar: PropTypes.string};
|
||||
@@ -344,6 +354,84 @@ describe('ReactTypeScriptClass', function() {
|
||||
test(React.createElement(StateBasedOnProps), 'SPAN', 'bar');
|
||||
});
|
||||
|
||||
it('sets initial state with value returned by static getDerivedStateFromProps', function() {
|
||||
class Foo extends React.Component {
|
||||
state = {
|
||||
foo: null,
|
||||
bar: null,
|
||||
};
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
return {
|
||||
foo: nextProps.foo,
|
||||
bar: 'bar',
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return React.createElement('div', {className: `${this.state.foo} ${this.state.bar}`});
|
||||
}
|
||||
}
|
||||
test(React.createElement(Foo, {foo: "foo"}), 'DIV', 'foo bar');
|
||||
});
|
||||
|
||||
it('warns if state not initialized before static getDerivedStateFromProps', function() {
|
||||
class Foo extends React.Component {
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
return {
|
||||
foo: nextProps.foo,
|
||||
bar: 'bar',
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return React.createElement('div', {className: `${this.state.foo} ${this.state.bar}`});
|
||||
}
|
||||
}
|
||||
expect(function() {
|
||||
ReactDOM.render(React.createElement(Foo, {foo: "foo"}), container);
|
||||
}).toWarnDev(
|
||||
'Foo: Did not properly initialize state during construction. ' +
|
||||
'Expected state to be an object, but it was undefined.'
|
||||
);
|
||||
});
|
||||
|
||||
it('updates initial state with values returned by static getDerivedStateFromProps', function() {
|
||||
class Foo extends React.Component {
|
||||
state = {
|
||||
foo: 'foo',
|
||||
bar: 'bar',
|
||||
};
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
return {
|
||||
foo: `not-${prevState.foo}`,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
return React.createElement('div', {className: `${this.state.foo} ${this.state.bar}`});
|
||||
}
|
||||
}
|
||||
test(React.createElement(Foo), 'DIV', 'not-foo bar');
|
||||
});
|
||||
|
||||
it('renders updated state with values returned by static getDerivedStateFromProps', function() {
|
||||
class Foo extends React.Component {
|
||||
state = {
|
||||
value: 'initial',
|
||||
};
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.update) {
|
||||
return {
|
||||
value: 'updated',
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
render() {
|
||||
return React.createElement('div', {className: this.state.value});
|
||||
}
|
||||
}
|
||||
test(React.createElement(Foo, {update:false}), 'DIV', 'initial');
|
||||
test(React.createElement(Foo, {update:true}), 'DIV', 'updated');
|
||||
});
|
||||
|
||||
it('renders based on context in the constructor', function() {
|
||||
test(React.createElement(ProvideChildContextTypes), 'SPAN', 'foo');
|
||||
});
|
||||
@@ -483,6 +571,16 @@ describe('ReactTypeScriptClass', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn when misspelling UNSAFE_componentWillReceiveProps', function() {
|
||||
expect(() =>
|
||||
test(React.createElement(MisspelledComponent3), 'SPAN', 'foo')
|
||||
).toWarnDev(
|
||||
'Warning: ' +
|
||||
'MisspelledComponent3 has a method called UNSAFE_componentWillRecieveProps(). ' +
|
||||
'Did you mean UNSAFE_componentWillReceiveProps()?'
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw AND warn when trying to access classic APIs', function() {
|
||||
const instance = test(
|
||||
React.createElement(Inner, {name: 'foo'}),
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactFeatureFlags;
|
||||
let createReactClass;
|
||||
|
||||
describe('create-react-class-integration', () => {
|
||||
beforeEach(() => {
|
||||
ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
||||
ReactFeatureFlags.warnAboutDeprecatedLifecycles = true;
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
createReactClass = require('create-react-class/factory')(
|
||||
React.Component,
|
||||
React.isValidElement,
|
||||
new React.Component().updater,
|
||||
);
|
||||
});
|
||||
|
||||
// TODO (RFC #6) Merge this back into createReactClassIntegration-test once
|
||||
// the 'warnAboutDeprecatedLifecycles' feature flag has been removed.
|
||||
it('isMounted works', () => {
|
||||
const ops = [];
|
||||
let instance;
|
||||
const Component = createReactClass({
|
||||
displayName: 'MyComponent',
|
||||
mixins: [
|
||||
{
|
||||
componentWillMount() {
|
||||
this.log('mixin.componentWillMount');
|
||||
},
|
||||
componentDidMount() {
|
||||
this.log('mixin.componentDidMount');
|
||||
},
|
||||
componentWillUpdate() {
|
||||
this.log('mixin.componentWillUpdate');
|
||||
},
|
||||
componentDidUpdate() {
|
||||
this.log('mixin.componentDidUpdate');
|
||||
},
|
||||
componentWillUnmount() {
|
||||
this.log('mixin.componentWillUnmount');
|
||||
},
|
||||
},
|
||||
],
|
||||
log(name) {
|
||||
ops.push(`${name}: ${this.isMounted()}`);
|
||||
},
|
||||
getInitialState() {
|
||||
this.log('getInitialState');
|
||||
return {};
|
||||
},
|
||||
componentWillMount() {
|
||||
this.log('componentWillMount');
|
||||
},
|
||||
componentDidMount() {
|
||||
this.log('componentDidMount');
|
||||
},
|
||||
componentWillUpdate() {
|
||||
this.log('componentWillUpdate');
|
||||
},
|
||||
componentDidUpdate() {
|
||||
this.log('componentDidUpdate');
|
||||
},
|
||||
componentWillUnmount() {
|
||||
this.log('componentWillUnmount');
|
||||
},
|
||||
render() {
|
||||
instance = this;
|
||||
this.log('render');
|
||||
return <div />;
|
||||
},
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
// TODO (RFC #6) The below lifecycle warnings are unavoidable for now,
|
||||
// Until create-react-class recognizes the UNSAFE_* methods.
|
||||
// (If we try to use them before them, it will error because
|
||||
// we are defining the same method twice.)
|
||||
// Update the above component to use the new UNSAFE_* methods
|
||||
// (and remove the expected warnings) once create-react-class has been updated.
|
||||
expect(() => ReactDOM.render(<Component />, container)).toWarnDev([
|
||||
'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
|
||||
'clean up subscriptions and pending requests in componentWillUnmount ' +
|
||||
'to prevent memory leaks.',
|
||||
'Warning: MyComponent: componentWillMount() is deprecated and will be ' +
|
||||
'removed in the next major version.',
|
||||
]);
|
||||
|
||||
expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
|
||||
'Warning: MyComponent: componentWillUpdate() is deprecated and will be ' +
|
||||
'removed in the next major version.',
|
||||
);
|
||||
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
instance.log('after unmount');
|
||||
expect(ops).toEqual([
|
||||
'getInitialState: false',
|
||||
'mixin.componentWillMount: false',
|
||||
'componentWillMount: false',
|
||||
'render: false',
|
||||
'mixin.componentDidMount: true',
|
||||
'componentDidMount: true',
|
||||
'mixin.componentWillUpdate: true',
|
||||
'componentWillUpdate: true',
|
||||
'render: true',
|
||||
'mixin.componentDidUpdate: true',
|
||||
'componentDidUpdate: true',
|
||||
'mixin.componentWillUnmount: true',
|
||||
'componentWillUnmount: true',
|
||||
'after unmount: false',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -17,6 +17,7 @@ let createReactClass;
|
||||
|
||||
describe('create-react-class-integration', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
PropTypes = require('prop-types');
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
@@ -151,6 +152,23 @@ describe('create-react-class-integration', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// TODO (RFC #6) Reenable after create-react-class updated.
|
||||
xit('should warn when misspelling UNSAFE_componentWillReceiveProps', () => {
|
||||
expect(() =>
|
||||
createReactClass({
|
||||
UNSAFE_componentWillRecieveProps: function() {
|
||||
return false;
|
||||
},
|
||||
render: function() {
|
||||
return <div />;
|
||||
},
|
||||
}),
|
||||
).toWarnDev(
|
||||
'Warning: A component has a method called UNSAFE_componentWillRecieveProps(). ' +
|
||||
'Did you mean UNSAFE_componentWillReceiveProps()?',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw if a reserved property is in statics', () => {
|
||||
expect(function() {
|
||||
createReactClass({
|
||||
@@ -175,7 +193,6 @@ describe('create-react-class-integration', () => {
|
||||
});
|
||||
|
||||
// TODO: Consider actually moving these to statics or drop this unit test.
|
||||
|
||||
xit('should warn when using deprecated non-static spec keys', () => {
|
||||
expect(() =>
|
||||
createReactClass({
|
||||
@@ -251,6 +268,23 @@ describe('create-react-class-integration', () => {
|
||||
expect(instance.state.occupation).toEqual('clown');
|
||||
});
|
||||
|
||||
it('should work with getDerivedStateFromProps() return values', () => {
|
||||
const Component = createReactClass({
|
||||
getInitialState() {
|
||||
return {};
|
||||
},
|
||||
render: function() {
|
||||
return <span />;
|
||||
},
|
||||
});
|
||||
Component.getDerivedStateFromProps = () => {
|
||||
return {occupation: 'clown'};
|
||||
};
|
||||
let instance = <Component />;
|
||||
instance = ReactTestUtils.renderIntoDocument(instance);
|
||||
expect(instance.state.occupation).toEqual('clown');
|
||||
});
|
||||
|
||||
it('renders based on context getInitialState', () => {
|
||||
const Foo = createReactClass({
|
||||
contextTypes: {
|
||||
@@ -346,85 +380,73 @@ describe('create-react-class-integration', () => {
|
||||
expect(ops).toEqual(['Render: 0', 'Render: 1', 'Callback: 1']);
|
||||
});
|
||||
|
||||
it('isMounted works', () => {
|
||||
const ops = [];
|
||||
let instance;
|
||||
it('getDerivedStateFromProps updates state when props change', () => {
|
||||
const Component = createReactClass({
|
||||
displayName: 'MyComponent',
|
||||
mixins: [
|
||||
{
|
||||
componentWillMount() {
|
||||
this.log('mixin.componentWillMount');
|
||||
},
|
||||
componentDidMount() {
|
||||
this.log('mixin.componentDidMount');
|
||||
},
|
||||
componentWillUpdate() {
|
||||
this.log('mixin.componentWillUpdate');
|
||||
},
|
||||
componentDidUpdate() {
|
||||
this.log('mixin.componentDidUpdate');
|
||||
},
|
||||
componentWillUnmount() {
|
||||
this.log('mixin.componentWillUnmount');
|
||||
},
|
||||
},
|
||||
],
|
||||
log(name) {
|
||||
ops.push(`${name}: ${this.isMounted()}`);
|
||||
},
|
||||
getInitialState() {
|
||||
this.log('getInitialState');
|
||||
return {};
|
||||
},
|
||||
componentWillMount() {
|
||||
this.log('componentWillMount');
|
||||
},
|
||||
componentDidMount() {
|
||||
this.log('componentDidMount');
|
||||
},
|
||||
componentWillUpdate() {
|
||||
this.log('componentWillUpdate');
|
||||
},
|
||||
componentDidUpdate() {
|
||||
this.log('componentDidUpdate');
|
||||
},
|
||||
componentWillUnmount() {
|
||||
this.log('componentWillUnmount');
|
||||
return {
|
||||
count: 1,
|
||||
};
|
||||
},
|
||||
render() {
|
||||
instance = this;
|
||||
this.log('render');
|
||||
return <div />;
|
||||
return <div>count:{this.state.count}</div>;
|
||||
},
|
||||
});
|
||||
Component.getDerivedStateFromProps = (nextProps, prevState) => ({
|
||||
count: prevState.count + nextProps.incrementBy,
|
||||
});
|
||||
|
||||
const container = document.createElement('div');
|
||||
|
||||
expect(() => ReactDOM.render(<Component />, container)).toWarnDev(
|
||||
'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
|
||||
'clean up subscriptions and pending requests in componentWillUnmount ' +
|
||||
'to prevent memory leaks.',
|
||||
const instance = ReactDOM.render(
|
||||
<div>
|
||||
<Component incrementBy={0} />
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
expect(instance.textContent).toEqual('count:1');
|
||||
ReactDOM.render(
|
||||
<div>
|
||||
<Component incrementBy={2} />
|
||||
</div>,
|
||||
container,
|
||||
);
|
||||
expect(instance.textContent).toEqual('count:3');
|
||||
});
|
||||
|
||||
ReactDOM.render(<Component />, container);
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
instance.log('after unmount');
|
||||
expect(ops).toEqual([
|
||||
'getInitialState: false',
|
||||
'mixin.componentWillMount: false',
|
||||
'componentWillMount: false',
|
||||
'render: false',
|
||||
'mixin.componentDidMount: true',
|
||||
'componentDidMount: true',
|
||||
'mixin.componentWillUpdate: true',
|
||||
'componentWillUpdate: true',
|
||||
'render: true',
|
||||
'mixin.componentDidUpdate: true',
|
||||
'componentDidUpdate: true',
|
||||
'mixin.componentWillUnmount: true',
|
||||
'componentWillUnmount: true',
|
||||
'after unmount: false',
|
||||
]);
|
||||
it('should support the new static getDerivedStateFromProps method', () => {
|
||||
let instance;
|
||||
const Component = createReactClass({
|
||||
statics: {
|
||||
getDerivedStateFromProps: function() {
|
||||
return {foo: 'bar'};
|
||||
},
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
instance = this;
|
||||
return null;
|
||||
},
|
||||
});
|
||||
ReactDOM.render(<Component />, document.createElement('div'));
|
||||
expect(instance.state.foo).toBe('bar');
|
||||
});
|
||||
|
||||
it('should warn if state is not properly initialized before getDerivedStateFromProps', () => {
|
||||
const Component = createReactClass({
|
||||
statics: {
|
||||
getDerivedStateFromProps: function() {
|
||||
return null;
|
||||
},
|
||||
},
|
||||
render: function() {
|
||||
return null;
|
||||
},
|
||||
});
|
||||
expect(() =>
|
||||
ReactDOM.render(<Component />, document.createElement('div')),
|
||||
).toWarnDev('Did not properly initialize state during construction.');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,9 @@ export const enablePersistentReconciler = false;
|
||||
// Helps identify side effects in begin-phase lifecycle hooks and setState reducers:
|
||||
export const debugRenderPhaseSideEffects = false;
|
||||
|
||||
// Warn about deprecated, async-unsafe lifecycles; relates to RFC #6:
|
||||
export const warnAboutDeprecatedLifecycles = false;
|
||||
|
||||
// Only used in www builds.
|
||||
export function addUserTimingListener() {
|
||||
invariant(false, 'Not implemented.');
|
||||
|
||||
@@ -16,6 +16,7 @@ export const debugRenderPhaseSideEffects = false;
|
||||
export const enableAsyncSubtreeAPI = true;
|
||||
export const enableCreateRoot = false;
|
||||
export const enableUserTimingAPI = __DEV__;
|
||||
export const warnAboutDeprecatedLifecycles = false;
|
||||
|
||||
// React Native CS uses persistent reconciler.
|
||||
export const enableMutatingReconciler = false;
|
||||
|
||||
@@ -13,7 +13,10 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.native';
|
||||
|
||||
// Re-export dynamic flags from the fbsource version.
|
||||
export const {debugRenderPhaseSideEffects} = require('ReactFeatureFlags');
|
||||
export const {
|
||||
debugRenderPhaseSideEffects,
|
||||
warnAboutDeprecatedLifecycles,
|
||||
} = require('ReactFeatureFlags');
|
||||
|
||||
// The rest of the flags are static for better dead code elimination.
|
||||
export const enableAsyncSubtreeAPI = true;
|
||||
|
||||
@@ -11,7 +11,10 @@ import typeof * as FeatureFlagsType from 'shared/ReactFeatureFlags';
|
||||
import typeof * as FeatureFlagsShimType from './ReactFeatureFlags.www';
|
||||
|
||||
// Re-export dynamic flags from the www version.
|
||||
export const {debugRenderPhaseSideEffects} = require('ReactFeatureFlags');
|
||||
export const {
|
||||
debugRenderPhaseSideEffects,
|
||||
warnAboutDeprecatedLifecycles,
|
||||
} = require('ReactFeatureFlags');
|
||||
|
||||
// The rest of the flags are static for better dead code elimination.
|
||||
export const enableAsyncSubtreeAPI = true;
|
||||
|
||||
Reference in New Issue
Block a user