Track which lifecycle method we're in during DEV

Use this to detect setState inside of render instead of relying on
the owner.
This commit is contained in:
Andrew Clark
2017-02-24 11:02:21 -08:00
parent ebfde985d9
commit 309dea5b4c
4 changed files with 47 additions and 8 deletions
@@ -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<T, P, I, TI, PI, C, CX, PL>(
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<T, P, I, TI, PI, C, CX, PL>(
// 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.
@@ -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<T, P, I, TI, PI, C, CX, PL>(
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)
);
}
}
@@ -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<T, P, I, TI, PI, C, CX, PL>(config : HostConfig<T, P,
ReactCurrentOwner.current = null;
if (__DEV__) {
ReactDebugCurrentFiber.current = null;
ReactDebugLifeCycle.current = null;
ReactDebugLifeCycle.phase = null;
onEndProcessingChildContext();
}
// It is no longer valid because this unit of work failed.
@@ -0,0 +1,24 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDebugLifeCycle
* @flow
*/
'use strict';
import type { Fiber } from 'ReactFiber';
type LifeCyclePhase = 'render';
const ReactDebugLifeCycle = {
current: (null : Fiber | null),
phase: (null : LifeCyclePhase | null),
};
module.exports = ReactDebugLifeCycle;