diff --git a/packages/react-dom/src/server/ReactPartialRenderer.js b/packages/react-dom/src/server/ReactPartialRenderer.js index 62ea2e042824f..254d049cc42b8 100644 --- a/packages/react-dom/src/server/ReactPartialRenderer.js +++ b/packages/react-dom/src/server/ReactPartialRenderer.js @@ -72,6 +72,7 @@ if (__DEV__) { var {ReactDebugCurrentFrame} = require('shared/ReactGlobalSharedState'); var currentDebugStack = null; var currentDebugElementStack = null; + var didWarnAboutNoopUpdateForComponent = {}; var setCurrentDebugStack = function(stack: Array) { var frame: Frame = stack[stack.length - 1]; currentDebugElementStack = ((frame: any): FrameDev).debugElementStack; @@ -175,6 +176,13 @@ function warnNoop( ) { if (__DEV__) { var constructor = publicInstance.constructor; + const componentName = + (constructor && getComponentName(constructor)) || 'ReactClass'; + const warningKey = `${callerName}_${componentName}`; + if (didWarnAboutNoopUpdateForComponent[warningKey]) { + return; + } + warning( false, '%s(...): Can only update a mounting component. ' + @@ -182,8 +190,9 @@ function warnNoop( 'This is a no-op.\n\nPlease check the code for the %s component.', callerName, callerName, - (constructor && getComponentName(constructor)) || 'ReactClass', + componentName, ); + didWarnAboutNoopUpdateForComponent[warningKey] = true; } } diff --git a/packages/react-reconciler/src/ReactFiberClassComponent.js b/packages/react-reconciler/src/ReactFiberClassComponent.js index 396be01fb8ab0..2a1e9a64ee3b9 100644 --- a/packages/react-reconciler/src/ReactFiberClassComponent.js +++ b/packages/react-reconciler/src/ReactFiberClassComponent.js @@ -41,6 +41,7 @@ if (__DEV__) { var warning = require('fbjs/lib/warning'); var {startPhaseTimer, stopPhaseTimer} = require('./ReactDebugFiberPerf'); + var didWarnAboutStateAssignmentForComponent = {}; var warnOnInvalidCallback = function(callback: mixed, callerName: string) { warning( @@ -393,13 +394,17 @@ module.exports = function( if (instance.state !== oldState) { if (__DEV__) { - warning( - false, - '%s.componentWillReceiveProps(): Assigning directly to ' + - "this.state is deprecated (except inside a component's " + - 'constructor). Use setState instead.', - getComponentName(workInProgress), - ); + const componentName = getComponentName(workInProgress) || 'Component'; + if (!didWarnAboutStateAssignmentForComponent[componentName]) { + warning( + false, + '%s.componentWillReceiveProps(): Assigning directly to ' + + "this.state is deprecated (except inside a component's " + + 'constructor). Use setState instead.', + componentName, + ); + didWarnAboutStateAssignmentForComponent[componentName] = true; + } } updater.enqueueReplaceState(instance, instance.state, null); } diff --git a/packages/react-reconciler/src/ReactFiberScheduler.js b/packages/react-reconciler/src/ReactFiberScheduler.js index 67a87c9a4a3f8..21f92db452ab8 100644 --- a/packages/react-reconciler/src/ReactFiberScheduler.js +++ b/packages/react-reconciler/src/ReactFiberScheduler.js @@ -101,34 +101,42 @@ if (__DEV__) { } = require('./ReactDebugFiberPerf'); var didWarnAboutStateTransition = false; + var didWarnSetStateChildContext = false; + var didWarnStateUpdateForUnmountedComponent = {}; + + var warnAboutUpdateOnUnmounted = function(fiber: Fiber) { + const componentName = getComponentName(fiber) || 'ReactClass'; + if (didWarnStateUpdateForUnmountedComponent[componentName]) { + return; + } - var warnAboutUpdateOnUnmounted = function( - instance: React$ComponentType, - ) { - const ctor = instance.constructor; warning( false, - 'Can only update a mounted or mounting component. This usually means ' + - 'you called setState, replaceState, or forceUpdate on an unmounted ' + - 'component. This is a no-op.\n\nPlease check the code for the ' + - '%s component.', - (ctor && (ctor.displayName || ctor.name)) || 'ReactClass', + 'Can only update a mounted or mounting ' + + 'component. This usually means you called setState, replaceState, ' + + 'or forceUpdate on an unmounted component. This is a no-op.\n\nPlease ' + + 'check the code for the %s component.', + componentName, ); + didWarnStateUpdateForUnmountedComponent[componentName] = true; }; var warnAboutInvalidUpdates = function(instance: React$ComponentType) { switch (ReactDebugCurrentFiber.phase) { case 'getChildContext': + if (didWarnSetStateChildContext) { + return; + } warning( false, 'setState(...): Cannot call setState() inside getChildContext()', ); + didWarnSetStateChildContext = true; break; case 'render': if (didWarnAboutStateTransition) { return; } - didWarnAboutStateTransition = true; warning( false, 'Cannot update during an existing state transition (such as within ' + @@ -136,6 +144,7 @@ if (__DEV__) { 'be a pure function of props and state; constructor side-effects are ' + 'an anti-pattern, but can be moved to `componentWillMount`.', ); + didWarnAboutStateTransition = true; break; } }; @@ -1229,7 +1238,7 @@ module.exports = function( } else { if (__DEV__) { if (!isErrorRecovery && fiber.tag === ClassComponent) { - warnAboutUpdateOnUnmounted(fiber.stateNode); + warnAboutUpdateOnUnmounted(fiber); } } return; diff --git a/packages/react-reconciler/src/ReactFiberUpdateQueue.js b/packages/react-reconciler/src/ReactFiberUpdateQueue.js index 1fd6fa18cb8b2..9773bdc9e8380 100644 --- a/packages/react-reconciler/src/ReactFiberUpdateQueue.js +++ b/packages/react-reconciler/src/ReactFiberUpdateQueue.js @@ -20,6 +20,7 @@ const {NoWork} = require('./ReactFiberExpirationTime'); if (__DEV__) { var warning = require('fbjs/lib/warning'); + var didWarnUpdateInsideUpdate = false; } type PartialState = @@ -132,7 +133,10 @@ function insertUpdateIntoFiber( // Warn if an update is scheduled from inside an updater function. if (__DEV__) { - if (queue1.isProcessing || (queue2 !== null && queue2.isProcessing)) { + if ( + (queue1.isProcessing || (queue2 !== null && queue2.isProcessing)) && + !didWarnUpdateInsideUpdate + ) { warning( false, 'An update (setState, replaceState, or forceUpdate) was scheduled ' + @@ -140,6 +144,7 @@ function insertUpdateIntoFiber( 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.', ); + didWarnUpdateInsideUpdate = true; } } diff --git a/packages/react/src/ReactNoopUpdateQueue.js b/packages/react/src/ReactNoopUpdateQueue.js index 5b0631dd08617..56391be1abbaa 100644 --- a/packages/react/src/ReactNoopUpdateQueue.js +++ b/packages/react/src/ReactNoopUpdateQueue.js @@ -9,11 +9,19 @@ if (__DEV__) { var warning = require('fbjs/lib/warning'); + var didWarnStateUpdateForUnmountedComponent = {}; } function warnNoop(publicInstance, callerName) { if (__DEV__) { var constructor = publicInstance.constructor; + const componentName = + (constructor && (constructor.displayName || constructor.name)) || + 'ReactClass'; + const warningKey = `${callerName}_${componentName}`; + if (didWarnStateUpdateForUnmountedComponent[warningKey]) { + return; + } warning( false, '%s(...): Can only update a mounted or mounting component. ' + @@ -21,9 +29,9 @@ function warnNoop(publicInstance, callerName) { 'This is a no-op.\n\nPlease check the code for the %s component.', callerName, callerName, - (constructor && (constructor.displayName || constructor.name)) || - 'ReactClass', + componentName, ); + didWarnStateUpdateForUnmountedComponent[warningKey] = true; } }