Removed dev warnings from shallow renderer. (#12433)

This commit is contained in:
Brian Vaughn 2018-03-22 11:32:37 -07:00 committed by GitHub
parent c1308adb4b
commit 8c20615b06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 1 additions and 418 deletions

View File

@ -8,33 +8,12 @@
import React from 'react'; import React from 'react';
import {isForwardRef} from 'react-is'; import {isForwardRef} from 'react-is';
import {warnAboutDeprecatedLifecycles} from 'shared/ReactFeatureFlags';
import describeComponentFrame from 'shared/describeComponentFrame'; import describeComponentFrame from 'shared/describeComponentFrame';
import getComponentName from 'shared/getComponentName'; import getComponentName from 'shared/getComponentName';
import emptyObject from 'fbjs/lib/emptyObject'; import emptyObject from 'fbjs/lib/emptyObject';
import invariant from 'fbjs/lib/invariant'; import invariant from 'fbjs/lib/invariant';
import lowPriorityWarning from 'shared/lowPriorityWarning';
import shallowEqual from 'fbjs/lib/shallowEqual'; import shallowEqual from 'fbjs/lib/shallowEqual';
import checkPropTypes from 'prop-types/checkPropTypes'; import checkPropTypes from 'prop-types/checkPropTypes';
import warning from 'fbjs/lib/warning';
let didWarnAboutLegacyWillMount;
let didWarnAboutLegacyWillReceiveProps;
let didWarnAboutLegacyWillUpdate;
let didWarnAboutUndefinedDerivedState;
let didWarnAboutUninitializedState;
let didWarnAboutLegacyLifecyclesAndDerivedState;
if (__DEV__) {
if (warnAboutDeprecatedLifecycles) {
didWarnAboutLegacyWillMount = {};
didWarnAboutLegacyWillReceiveProps = {};
didWarnAboutLegacyWillUpdate = {};
}
didWarnAboutUndefinedDerivedState = {};
didWarnAboutUninitializedState = {};
didWarnAboutLegacyLifecyclesAndDerivedState = {};
}
class ReactShallowRenderer { class ReactShallowRenderer {
static createRenderer = function() { static createRenderer = function() {
@ -106,28 +85,6 @@ class ReactShallowRenderer {
this._updater, 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); this._updateStateFromStaticLifecycle(element.props);
if (element.type.hasOwnProperty('contextTypes')) { if (element.type.hasOwnProperty('contextTypes')) {
@ -183,31 +140,6 @@ class ReactShallowRenderer {
const beforeState = this._newState; const beforeState = this._newState;
if (typeof this._instance.componentWillMount === 'function') { if (typeof this._instance.componentWillMount === 'function') {
if (__DEV__) {
// Don't warn about react-lifecycles-compat polyfilled components
if (
warnAboutDeprecatedLifecycles &&
this._instance.componentWillMount.__suppressDeprecationWarning !==
true
) {
const componentName = getName(element.type, this._instance);
if (!didWarnAboutLegacyWillMount[componentName]) {
lowPriorityWarning(
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;
}
}
}
// In order to support react-lifecycles-compat polyfilled components, // In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for any component with the new gDSFP. // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
if (typeof element.type.getDerivedStateFromProps !== 'function') { if (typeof element.type.getDerivedStateFromProps !== 'function') {
@ -242,26 +174,6 @@ class ReactShallowRenderer {
if (oldProps !== props) { if (oldProps !== props) {
if (typeof this._instance.componentWillReceiveProps === 'function') { if (typeof this._instance.componentWillReceiveProps === 'function') {
if (__DEV__) {
if (warnAboutDeprecatedLifecycles) {
const componentName = getName(element.type, this._instance);
if (!didWarnAboutLegacyWillReceiveProps[componentName]) {
lowPriorityWarning(
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;
}
}
}
// In order to support react-lifecycles-compat polyfilled components, // In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for any component with the new gDSFP. // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
if (typeof element.type.getDerivedStateFromProps !== 'function') { if (typeof element.type.getDerivedStateFromProps !== 'function') {
@ -300,26 +212,6 @@ class ReactShallowRenderer {
if (shouldUpdate) { if (shouldUpdate) {
if (typeof this._instance.componentWillUpdate === 'function') { if (typeof this._instance.componentWillUpdate === 'function') {
if (__DEV__) {
if (warnAboutDeprecatedLifecycles) {
const componentName = getName(element.type, this._instance);
if (!didWarnAboutLegacyWillUpdate[componentName]) {
lowPriorityWarning(
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;
}
}
}
// In order to support react-lifecycles-compat polyfilled components, // In order to support react-lifecycles-compat polyfilled components,
// Unsafe lifecycles should not be invoked for any component with the new gDSFP. // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
if (typeof type.getDerivedStateFromProps !== 'function') { if (typeof type.getDerivedStateFromProps !== 'function') {
@ -351,87 +243,12 @@ class ReactShallowRenderer {
const {type} = this._element; const {type} = this._element;
if (typeof type.getDerivedStateFromProps === 'function') { if (typeof type.getDerivedStateFromProps === 'function') {
if (__DEV__) {
const instance = this._instance;
// If getDerivedStateFromProps() is defined, "unsafe" lifecycles won't be called.
// Warn about these lifecycles if they are present.
// Don't warn about react-lifecycles-compat polyfilled methods though.
let foundWillMountName = null;
let foundWillReceivePropsName = null;
let foundWillUpdateName = null;
if (
typeof instance.componentWillMount === 'function' &&
instance.componentWillMount.__suppressDeprecationWarning !== true
) {
foundWillMountName = 'componentWillMount';
} else if (typeof instance.UNSAFE_componentWillMount === 'function') {
foundWillMountName = 'UNSAFE_componentWillMount';
}
if (
typeof instance.componentWillReceiveProps === 'function' &&
instance.componentWillReceiveProps.__suppressDeprecationWarning !==
true
) {
foundWillReceivePropsName = 'componentWillReceiveProps';
} else if (
typeof instance.UNSAFE_componentWillReceiveProps === 'function'
) {
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
}
if (typeof instance.componentWillUpdate === 'function') {
foundWillUpdateName = 'componentWillUpdate';
} else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
}
if (
foundWillMountName !== null ||
foundWillReceivePropsName !== null ||
foundWillUpdateName !== null
) {
const componentName = getName(type, instance) || 'Component';
if (!didWarnAboutLegacyLifecyclesAndDerivedState[componentName]) {
warning(
false,
'Unsafe legacy lifecycles will not be called for components using ' +
'the new getDerivedStateFromProps() API.\n\n' +
'%s uses getDerivedStateFromProps() but also contains the following legacy lifecycles:' +
'%s%s%s\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
componentName,
foundWillMountName !== null ? `\n ${foundWillMountName}` : '',
foundWillReceivePropsName !== null
? `\n ${foundWillReceivePropsName}`
: '',
foundWillUpdateName !== null ? `\n ${foundWillUpdateName}` : '',
);
didWarnAboutLegacyLifecyclesAndDerivedState[componentName] = true;
}
}
}
const partialState = type.getDerivedStateFromProps.call( const partialState = type.getDerivedStateFromProps.call(
null, null,
props, props,
this._instance.state, 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) { if (partialState != null) {
const oldState = this._newState || this._instance.state; const oldState = this._newState || this._instance.state;
const newState = Object.assign({}, oldState, partialState); const newState = Object.assign({}, oldState, partialState);

View File

@ -1,82 +0,0 @@
/**
* 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');
});
afterEach(() => {
jest.resetModules();
});
// 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 />),
).toLowPriorityWarnDev(
'Warning: ComponentWithWarnings: componentWillMount() is deprecated and will ' +
'be removed in the next major version.',
);
expect(() =>
shallowRenderer.render(<ComponentWithWarnings />),
).toLowPriorityWarnDev([
'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 />);
});
describe('react-lifecycles-compat', () => {
const polyfill = require('react-lifecycles-compat');
it('should not warn about deprecated cWM/cWRP for polyfilled components', () => {
class PolyfilledComponent extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
render() {
return null;
}
}
polyfill(PolyfilledComponent);
const shallowRenderer = createRenderer();
shallowRenderer.render(<PolyfilledComponent />);
});
});
});

View File

@ -125,99 +125,7 @@ describe('ReactShallowRenderer', () => {
} }
const shallowRenderer = createRenderer(); const shallowRenderer = createRenderer();
expect(() => shallowRenderer.render(<Component />)).toWarnDev( shallowRenderer.render(<Component />);
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.',
);
});
it('should warn about deprecated lifecycles (cWM/cWRP/cWU) if new static gDSFP is present', () => {
let shallowRenderer;
class AllLegacyLifecycles extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
componentWillMount() {}
UNSAFE_componentWillReceiveProps() {}
componentWillUpdate() {}
render() {
return null;
}
}
shallowRenderer = createRenderer();
expect(() => shallowRenderer.render(<AllLegacyLifecycles />)).toWarnDev(
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
'AllLegacyLifecycles uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
' componentWillMount\n' +
' UNSAFE_componentWillReceiveProps\n' +
' componentWillUpdate\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
);
class WillMount extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
UNSAFE_componentWillMount() {}
render() {
return null;
}
}
shallowRenderer = createRenderer();
expect(() => shallowRenderer.render(<WillMount />)).toWarnDev(
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
'WillMount uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
' UNSAFE_componentWillMount\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
);
class WillMountAndUpdate extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
componentWillMount() {}
UNSAFE_componentWillUpdate() {}
render() {
return null;
}
}
shallowRenderer = createRenderer();
expect(() => shallowRenderer.render(<WillMountAndUpdate />)).toWarnDev(
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
'WillMountAndUpdate uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
' componentWillMount\n' +
' UNSAFE_componentWillUpdate\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
);
class WillReceiveProps extends React.Component {
state = {};
static getDerivedStateFromProps() {
return null;
}
componentWillReceiveProps() {}
render() {
return null;
}
}
shallowRenderer = createRenderer();
expect(() => shallowRenderer.render(<WillReceiveProps />)).toWarnDev(
'Unsafe legacy lifecycles will not be called for components using the new getDerivedStateFromProps() API.\n\n' +
'WillReceiveProps uses getDerivedStateFromProps() but also contains the following legacy lifecycles:\n' +
' componentWillReceiveProps\n\n' +
'The above lifecycles should be removed. Learn more about this warning here:\n' +
'https://fb.me/react-async-component-lifecycle-hooks',
);
}); });
it('should only render 1 level deep', () => { it('should only render 1 level deep', () => {
@ -1314,66 +1222,6 @@ describe('ReactShallowRenderer', () => {
expect(shallowRenderer.getMountedInstance().state).toBeNull(); 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 uses getDerivedStateFromProps() but also contains the following legacy lifecycles',
);
// 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 />);
});
it('should invoke both deprecated and new lifecycles if both are present', () => { it('should invoke both deprecated and new lifecycles if both are present', () => {
const log = []; const log = [];