diff --git a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js index cde3ab7a4f4f3..62ad2e01ba19d 100644 --- a/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js +++ b/packages/react-dom/src/__tests__/ReactComponentLifeCycle-test.js @@ -198,6 +198,27 @@ describe('ReactComponentLifeCycle', () => { }).not.toThrow(); }); + it("warns if setting 'this.state = props'", () => { + class StatefulComponent extends React.Component { + constructor(props, context) { + super(props, context); + this.state = props; + } + render() { + return
; + } + } + + expect(() => { + ReactTestUtils.renderIntoDocument(); + }).toWarnDev( + 'StatefulComponent: It is not recommended to assign props directly to state ' + + "because updates to props won't be reflected in state. " + + 'In most cases, it is better to use props directly.', + {withoutStack: true}, + ); + }); + it('should not allow update state inside of getInitialState', () => { class StatefulComponent extends React.Component { constructor(props, context) { diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 334bae2b72cc3..7646a51a70d12 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -64,12 +64,14 @@ let didWarnAboutLegacyLifecyclesAndDerivedState; let didWarnAboutUndefinedDerivedState; let warnOnUndefinedDerivedState; let warnOnInvalidCallback; +let didWarnAboutDirectlyAssigningPropsToState; if (__DEV__) { didWarnAboutStateAssignmentForComponent = new Set(); didWarnAboutUninitializedState = new Set(); didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set(); didWarnAboutLegacyLifecyclesAndDerivedState = new Set(); + didWarnAboutDirectlyAssigningPropsToState = new Set(); didWarnAboutUndefinedDerivedState = new Set(); const didWarnOnInvalidCallback = new Set(); @@ -674,6 +676,20 @@ function mountClassInstance( instance.context = getMaskedContext(workInProgress, unmaskedContext); if (__DEV__) { + if (instance.state === instance.props) { + const componentName = getComponentName(ctor) || 'Component'; + if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) { + didWarnAboutDirectlyAssigningPropsToState.add(componentName); + warningWithoutStack( + false, + '%s: It is not recommended to assign props directly to state ' + + "because updates to props won't be reflected in state. " + + 'In most cases, it is better to use props directly.', + componentName, + ); + } + } + if (workInProgress.mode & StrictMode) { ReactStrictModeWarnings.recordUnsafeLifecycleWarnings( workInProgress,