From 309dea5b4c77f267734f3ddbf852929f2c64c061 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 22 Feb 2017 13:38:14 -0800 Subject: [PATCH] Track which lifecycle method we're in during DEV Use this to detect setState inside of render instead of relying on the owner. --- .../shared/fiber/ReactFiberBeginWork.js | 16 ++++++++++++- .../shared/fiber/ReactFiberReconciler.js | 10 ++++---- .../shared/fiber/ReactFiberScheduler.js | 5 +++- .../fiber/isomorphic/ReactDebugLifeCycle.js | 24 +++++++++++++++++++ 4 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 src/renderers/shared/fiber/isomorphic/ReactDebugLifeCycle.js diff --git a/src/renderers/shared/fiber/ReactFiberBeginWork.js b/src/renderers/shared/fiber/ReactFiberBeginWork.js index 73c4870a30..7b6d8dc78d 100644 --- a/src/renderers/shared/fiber/ReactFiberBeginWork.js +++ b/src/renderers/shared/fiber/ReactFiberBeginWork.js @@ -66,6 +66,7 @@ var invariant = require('invariant'); if (__DEV__) { var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber'); + var ReactDebugLifeCycle = require('ReactDebugLifeCycle'); var warning = require('warning'); var warnedAboutStatelessRefs = {}; } @@ -230,7 +231,11 @@ module.exports = function( if (__DEV__) { ReactCurrentOwner.current = workInProgress; + ReactDebugLifeCycle.current = workInProgress; + ReactDebugLifeCycle.phase = 'render'; nextChildren = fn(nextProps, context); + ReactDebugLifeCycle.current = null; + ReactDebugLifeCycle.phase = null; } else { nextChildren = fn(nextProps, context); } @@ -279,7 +284,16 @@ module.exports = function( // Rerender ReactCurrentOwner.current = workInProgress; - const nextChildren = instance.render(); + let nextChildren; + if (__DEV__) { + ReactDebugLifeCycle.current = workInProgress; + ReactDebugLifeCycle.phase = 'render'; + nextChildren = instance.render(); + ReactDebugLifeCycle.current = null; + ReactDebugLifeCycle.phase = null; + } else { + nextChildren = instance.render(); + } reconcileChildren(current, workInProgress, nextChildren); // Memoize props and state using the values we just used to render. // TODO: Restructure so we never read values from the instance. diff --git a/src/renderers/shared/fiber/ReactFiberReconciler.js b/src/renderers/shared/fiber/ReactFiberReconciler.js index 5d36992791..ede6832440 100644 --- a/src/renderers/shared/fiber/ReactFiberReconciler.js +++ b/src/renderers/shared/fiber/ReactFiberReconciler.js @@ -33,7 +33,7 @@ if (__DEV__) { var warning = require('warning'); var ReactFiberInstrumentation = require('ReactFiberInstrumentation'); var warning = require('warning'); - var ReactCurrentOwner = require('ReactCurrentOwner'); + var ReactDebugLifeCycle = require('ReactDebugLifeCycle'); var { getComponentName } = require('ReactFiberTreeReflection'); } @@ -148,16 +148,14 @@ module.exports = function( function scheduleTopLevelUpdate(current : Fiber, element : ReactNodeList, callback : ?Function) { if (__DEV__) { - const owner = ReactCurrentOwner.current; - if (owner && typeof owner.tag === 'number') { - const ownerFiber : Fiber = (owner : any); + if (ReactDebugLifeCycle.current !== null) { warning( - false, + ReactDebugLifeCycle.phase !== 'render', 'Render methods should be a pure function of props and state; ' + 'triggering nested component updates from render is not allowed. ' + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + 'Check the render method of %s.', - getComponentName(ownerFiber) + getComponentName(ReactDebugLifeCycle.current) ); } } diff --git a/src/renderers/shared/fiber/ReactFiberScheduler.js b/src/renderers/shared/fiber/ReactFiberScheduler.js index 8d60e0e336..d90dedcb0d 100644 --- a/src/renderers/shared/fiber/ReactFiberScheduler.js +++ b/src/renderers/shared/fiber/ReactFiberScheduler.js @@ -90,6 +90,7 @@ if (__DEV__) { var warning = require('warning'); var ReactFiberInstrumentation = require('ReactFiberInstrumentation'); var ReactDebugCurrentFiber = require('ReactDebugCurrentFiber'); + var ReactDebugLifeCycle = require('ReactDebugLifeCycle'); var { isProcessingChildContext, onEndProcessingChildContext, @@ -113,7 +114,7 @@ if (__DEV__) { false, 'setState(...): Cannot call setState() inside getChildContext()', ); - } else if (ReactCurrentOwner.current != null) { + } else if (ReactDebugLifeCycle.phase === 'render') { warning( false, 'Cannot update during an existing state transition (such as within ' + @@ -880,6 +881,8 @@ module.exports = function(config : HostConfig