From 2514bfa0f2e45fa93d6aa4966f037bff5f8b2ba1 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 23 Aug 2016 18:41:57 +0100 Subject: [PATCH 1/2] Ensure lifecycle timers are stopped on errors Fixes #7349 --- src/renderers/shared/ReactDebugTool.js | 6 - .../shared/__tests__/ReactPerf-test.js | 204 ++++++++++ .../reconciler/ReactCompositeComponent.js | 362 ++++++++---------- 3 files changed, 361 insertions(+), 211 deletions(-) diff --git a/src/renderers/shared/ReactDebugTool.js b/src/renderers/shared/ReactDebugTool.js index e976445c3d635..a59053d3a35d7 100644 --- a/src/renderers/shared/ReactDebugTool.js +++ b/src/renderers/shared/ReactDebugTool.js @@ -251,12 +251,6 @@ var ReactDebugTool = { endLifeCycleTimer(debugID, timerType); emitEvent('onEndLifeCycleTimer', debugID, timerType); }, - onError(debugID) { - if (currentTimerDebugID != null) { - endLifeCycleTimer(currentTimerDebugID, currentTimerType); - } - emitEvent('onError', debugID); - }, onBeginProcessingChildContext() { emitEvent('onBeginProcessingChildContext'); }, diff --git a/src/renderers/shared/__tests__/ReactPerf-test.js b/src/renderers/shared/__tests__/ReactPerf-test.js index 99e5c9494312e..70e5cd5f53fa3 100644 --- a/src/renderers/shared/__tests__/ReactPerf-test.js +++ b/src/renderers/shared/__tests__/ReactPerf-test.js @@ -516,4 +516,208 @@ describe('ReactPerf', function() { renderCount: 2, }]); }); + + it('should not print errant warnings if render() throws', () => { + var container = document.createElement('div'); + var thrownErr = new Error('Muhaha!'); + + class Evil extends React.Component { + render() { + throw thrownErr; + } + } + + ReactPerf.start(); + try { + ReactDOM.render( +
+ + +
, + container + ); + } catch (err) { + if (err !== thrownErr) { + throw err; + } + } + ReactPerf.stop(); + }); + + it('should not print errant warnings if componentWillMount() throws', () => { + var container = document.createElement('div'); + var thrownErr = new Error('Muhaha!'); + + class Evil extends React.Component { + componentWillMount() { + throw thrownErr; + } + render() { + return
; + } + } + + ReactPerf.start(); + try { + ReactDOM.render( +
+ + +
, + container + ); + } catch (err) { + if (err !== thrownErr) { + throw err; + } + } + ReactPerf.stop(); + }); + + it('should not print errant warnings if componentDidMount() throws', () => { + var container = document.createElement('div'); + var thrownErr = new Error('Muhaha!'); + + class Evil extends React.Component { + componentDidMount() { + throw thrownErr; + } + render() { + return
; + } + } + + ReactPerf.start(); + try { + ReactDOM.render( +
+ + +
, + container + ); + } catch (err) { + if (err !== thrownErr) { + throw err; + } + } + ReactPerf.stop(); + }); + + it('should not print errant warnings if portal throws in render()', () => { + var container = document.createElement('div'); + var thrownErr = new Error('Muhaha!'); + + class Evil extends React.Component { + render() { + throw thrownErr; + } + } + class EvilPortal extends React.Component { + componentWillMount() { + var portalContainer = document.createElement('div'); + ReactDOM.render(, portalContainer); + } + render() { + return
; + } + } + + ReactPerf.start(); + try { + ReactDOM.render( +
+ + +
, + container + ); + } catch (err) { + if (err !== thrownErr) { + throw err; + } + } + ReactDOM.unmountComponentAtNode(container); + ReactPerf.stop(); + }); + + it('should not print errant warnings if portal throws in componentWillMount()', () => { + var container = document.createElement('div'); + var thrownErr = new Error('Muhaha!'); + + class Evil extends React.Component { + componentWillMount() { + throw thrownErr; + } + render() { + return
; + } + } + class EvilPortal extends React.Component { + componentWillMount() { + var portalContainer = document.createElement('div'); + ReactDOM.render(, portalContainer); + } + render() { + return
; + } + } + + ReactPerf.start(); + try { + ReactDOM.render( +
+ + +
, + container + ); + } catch (err) { + if (err !== thrownErr) { + throw err; + } + } + ReactDOM.unmountComponentAtNode(container); + ReactPerf.stop(); + }); + + it('should not print errant warnings if portal throws in componentDidMount()', () => { + var container = document.createElement('div'); + var thrownErr = new Error('Muhaha!'); + + class Evil extends React.Component { + componentDidMount() { + throw thrownErr; + } + render() { + return
; + } + } + class EvilPortal extends React.Component { + componentWillMount() { + var portalContainer = document.createElement('div'); + ReactDOM.render(, portalContainer); + } + render() { + return
; + } + } + + ReactPerf.start(); + try { + ReactDOM.render( +
+ + +
, + container + ); + } catch (err) { + if (err !== thrownErr) { + throw err; + } + } + ReactDOM.unmountComponentAtNode(container); + ReactPerf.stop(); + }); }); diff --git a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js index d9de0a913debe..9cabf27653d12 100644 --- a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js @@ -62,40 +62,6 @@ function warnIfInvalidElement(Component, element) { } } -function invokeComponentDidMountWithTimer() { - var publicInstance = this._instance; - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'componentDidMount' - ); - } - publicInstance.componentDidMount(); - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'componentDidMount' - ); - } -} - -function invokeComponentDidUpdateWithTimer(prevProps, prevState, prevContext) { - var publicInstance = this._instance; - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'componentDidUpdate' - ); - } - publicInstance.componentDidUpdate(prevProps, prevState, prevContext); - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'componentDidUpdate' - ); - } -} - function shouldConstruct(Component) { return !!(Component.prototype && Component.prototype.isReactComponent); } @@ -104,6 +70,16 @@ function isPureComponent(Component) { return !!(Component.prototype && Component.prototype.isPureReactComponent); } +// Separated into a function to contain deoptimizations caused by try/finally. +function measureLifeCyclePerf(fn, debugID, timerType) { + ReactInstrumentation.debugTool.onBeginLifeCycleTimer(debugID, timerType); + try { + return fn(); + } finally { + ReactInstrumentation.debugTool.onEndLifeCycleTimer(debugID, timerType); + } +} + /** * ------------------ The Life-Cycle of a Composite Component ------------------ * @@ -362,9 +338,20 @@ var ReactCompositeComponentMixin = { markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context); } + var debugID = 0; + if (__DEV__) { + debugID = this._debugID; + } + if (inst.componentDidMount) { - if (__DEV__) { - transaction.getReactMountReady().enqueue(invokeComponentDidMountWithTimer, this); + if (__DEV__ && debugID !== 0) { + transaction.getReactMountReady().enqueue(() => { + measureLifeCyclePerf( + () => inst.componentDidMount(), + debugID, + 'componentDidMount' + ); + }, null); } else { transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); } @@ -408,47 +395,35 @@ var ReactCompositeComponentMixin = { updateQueue ) { var Component = this._currentElement.type; - var instanceOrElement; + + var debugID = 0; + if (__DEV__) { + debugID = this._debugID; + } + if (doConstruct) { - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'ctor' - ); - } - } - instanceOrElement = new Component(publicProps, publicContext, updateQueue); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'ctor' - ); - } + if (__DEV__ && debugID !== 0) { + return measureLifeCyclePerf( + () => new Component(publicProps, publicContext, updateQueue), + debugID, + 'ctor' + ); + } else { + return new Component(publicProps, publicContext, updateQueue); } + } + + // This can still be an instance in case of factory components + // but we'll count this as time spent rendering as the more common case. + if (__DEV__ && debugID !== 0) { + return measureLifeCyclePerf( + () => Component(publicProps, publicContext, updateQueue), + debugID, + 'render' + ); } else { - // This can still be an instance in case of factory components - // but we'll count this as time spent rendering as the more common case. - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'render' - ); - } - } - instanceOrElement = Component(publicProps, publicContext, updateQueue); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'render' - ); - } - } + return Component(publicProps, publicContext, updateQueue); } - return instanceOrElement; }, performInitialMountWithErrorHandling: function( @@ -463,11 +438,6 @@ var ReactCompositeComponentMixin = { try { markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context); } catch (e) { - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onError(); - } - } // Roll back to checkpoint, handle error (which may add items to the transaction), and take a new checkpoint transaction.rollback(checkpoint); this._instance.unstable_handleError(e); @@ -488,23 +458,21 @@ var ReactCompositeComponentMixin = { performInitialMount: function(renderedElement, hostParent, hostContainerInfo, transaction, context) { var inst = this._instance; + + var debugID = 0; + if (__DEV__) { + debugID = this._debugID; + } + if (inst.componentWillMount) { - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'componentWillMount' - ); - } - } - inst.componentWillMount(); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'componentWillMount' - ); - } + if (__DEV__ && debugID !== 0) { + measureLifeCyclePerf( + () => inst.componentWillMount(), + debugID, + 'componentWillMount' + ); + } else { + inst.componentWillMount(); } // When mounting, calls to `setState` by `componentWillMount` will set // `this._pendingStateQueue` without triggering a re-render. @@ -526,26 +494,18 @@ var ReactCompositeComponentMixin = { ); this._renderedComponent = child; - var selfDebugID = 0; - if (__DEV__) { - selfDebugID = this._debugID; - } var markup = ReactReconciler.mountComponent( child, transaction, hostParent, hostContainerInfo, this._processChildContext(context), - selfDebugID + debugID ); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onSetChildren( - this._debugID, - child._debugID !== 0 ? [child._debugID] : [] - ); - } + if (__DEV__ && debugID !== 0) { + var childDebugIDs = child._debugID !== 0 ? [child._debugID] : []; + ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs); } return markup; @@ -565,30 +525,29 @@ var ReactCompositeComponentMixin = { if (!this._renderedComponent) { return; } + var inst = this._instance; + var debugID = 0; + if (__DEV__) { + debugID = this._debugID; + } + if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) { inst._calledComponentWillUnmount = true; - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'componentWillUnmount' - ); - } - } + if (safely) { var name = this.getName() + '.componentWillUnmount()'; ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst)); } else { - inst.componentWillUnmount(); - } - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, + if (__DEV__ && debugID !== 0) { + measureLifeCyclePerf( + () => inst.componentWillUnmount(), + debugID, 'componentWillUnmount' ); + } else { + inst.componentWillUnmount(); } } } @@ -679,13 +638,21 @@ var ReactCompositeComponentMixin = { _processChildContext: function(currentContext) { var Component = this._currentElement.type; var inst = this._instance; - if (__DEV__) { - ReactInstrumentation.debugTool.onBeginProcessingChildContext(); - } - var childContext = inst.getChildContext && inst.getChildContext(); - if (__DEV__) { - ReactInstrumentation.debugTool.onEndProcessingChildContext(); + var childContext; + + if (inst.getChildContext) { + if (__DEV__) { + ReactInstrumentation.debugTool.onBeginProcessingChildContext(); + try { + childContext = inst.getChildContext(); + } finally { + ReactInstrumentation.debugTool.onEndProcessingChildContext(); + } + } else { + childContext = inst.getChildContext(); + } } + if (childContext) { invariant( typeof Component.childContextTypes === 'object', @@ -826,26 +793,23 @@ var ReactCompositeComponentMixin = { willReceive = true; } + var debugID = 0; + if (__DEV__) { + debugID = this._debugID; + } + // An update here will schedule an update but immediately set // _pendingStateQueue which will ensure that any state updates gets // immediately reconciled instead of waiting for the next batch. if (willReceive && inst.componentWillReceiveProps) { - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'componentWillReceiveProps' - ); - } - } - inst.componentWillReceiveProps(nextProps, nextContext); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'componentWillReceiveProps' - ); - } + if (__DEV__ && debugID !== 0) { + measureLifeCyclePerf( + () => inst.componentWillReceiveProps(nextProps, nextContext), + debugID, + 'componentWillReceiveProps', + ); + } else { + inst.componentWillReceiveProps(nextProps, nextContext); } } @@ -854,22 +818,14 @@ var ReactCompositeComponentMixin = { if (!this._pendingForceUpdate) { if (inst.shouldComponentUpdate) { - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'shouldComponentUpdate' - ); - } - } - shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'shouldComponentUpdate' - ); - } + if (__DEV__ && debugID !== 0) { + shouldUpdate = measureLifeCyclePerf( + () => inst.shouldComponentUpdate(nextProps, nextState, nextContext), + debugID, + 'shouldComponentUpdate' + ); + } else { + shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext); } } else { if (this._compositeType === CompositeTypes.PureClass) { @@ -973,23 +929,20 @@ var ReactCompositeComponentMixin = { prevContext = inst.context; } + var debugID = 0; + if (__DEV__) { + debugID = this._debugID; + } + if (inst.componentWillUpdate) { - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'componentWillUpdate' - ); - } - } - inst.componentWillUpdate(nextProps, nextState, nextContext); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'componentWillUpdate' - ); - } + if (__DEV__ && debugID !== 0) { + measureLifeCyclePerf( + () => inst.componentWillUpdate(nextProps, nextState, nextContext), + debugID, + 'componentWillUpdate' + ); + } else { + inst.componentWillUpdate(nextProps, nextState, nextContext); } } @@ -1002,11 +955,14 @@ var ReactCompositeComponentMixin = { this._updateRenderedComponent(transaction, unmaskedContext); if (hasComponentDidUpdate) { - if (__DEV__) { - transaction.getReactMountReady().enqueue( - invokeComponentDidUpdateWithTimer.bind(this, prevProps, prevState, prevContext), - this - ); + if (__DEV__ && debugID !== 0) { + transaction.getReactMountReady().enqueue(() => { + measureLifeCyclePerf( + () => inst.componentDidUpdate(prevProps, prevState, prevContext), + debugID, + 'componentDidUpdate' + ); + }, null); } else { transaction.getReactMountReady().enqueue( inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), @@ -1026,6 +982,12 @@ var ReactCompositeComponentMixin = { var prevComponentInstance = this._renderedComponent; var prevRenderedElement = prevComponentInstance._currentElement; var nextRenderedElement = this._renderValidatedComponent(); + + var debugID = 0; + if (__DEV__) { + debugID = this._debugID; + } + if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) { ReactReconciler.receiveComponent( prevComponentInstance, @@ -1045,26 +1007,18 @@ var ReactCompositeComponentMixin = { ); this._renderedComponent = child; - var selfDebugID = 0; - if (__DEV__) { - selfDebugID = this._debugID; - } var nextMarkup = ReactReconciler.mountComponent( child, transaction, this._hostParent, this._hostContainerInfo, this._processChildContext(context), - selfDebugID + debugID ); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onSetChildren( - this._debugID, - child._debugID !== 0 ? [child._debugID] : [] - ); - } + if (__DEV__ && debugID !== 0) { + var childDebugIDs = child._debugID !== 0 ? [child._debugID] : []; + ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs); } this._replaceNodeWithMarkup( @@ -1093,23 +1047,21 @@ var ReactCompositeComponentMixin = { */ _renderValidatedComponentWithoutOwnerOrContext: function() { var inst = this._instance; + var renderedComponent; + var debugID = 0; if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onBeginLifeCycleTimer( - this._debugID, - 'render' - ); - } + debugID = this._debugID; } - var renderedComponent = inst.render(); - if (__DEV__) { - if (this._debugID !== 0) { - ReactInstrumentation.debugTool.onEndLifeCycleTimer( - this._debugID, - 'render' - ); - } + + if (__DEV__ && debugID !== 0) { + renderedComponent = measureLifeCyclePerf( + () => inst.render(), + debugID, + 'render' + ); + } else { + renderedComponent = inst.render(); } if (__DEV__) { From 669839973557f848ce4e8e29e540d6c49a586f75 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 24 Aug 2016 13:34:45 +0100 Subject: [PATCH 2/2] Address review feedback and add an extra test --- .../dom/__tests__/ReactDOMProduction-test.js | 88 +++++++++++++++++ .../reconciler/ReactCompositeComponent.js | 95 ++++++++----------- 2 files changed, 126 insertions(+), 57 deletions(-) diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js index a50e7eedc01af..5b1d4ebf94835 100644 --- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js +++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js @@ -86,6 +86,94 @@ describe('ReactDOMProduction', function() { expect(container.childNodes.length).toBe(0); }); + it('should call lifecycle methods', function() { + var log = []; + class Component extends React.Component { + state = {y: 1}; + shouldComponentUpdate(nextProps, nextState) { + log.push(['shouldComponentUpdate', nextProps, nextState]); + return nextProps.x !== this.props.x || nextState.y !== this.state.y; + } + componentWillMount() { + log.push(['componentWillMount']); + } + componentDidMount() { + log.push(['componentDidMount']); + } + componentWillReceiveProps(nextProps) { + log.push(['componentWillReceiveProps', nextProps]); + } + componentWillUpdate(nextProps, nextState) { + log.push(['componentWillUpdate', nextProps, nextState]); + } + componentDidUpdate(prevProps, prevState) { + log.push(['componentDidUpdate', prevProps, prevState]); + } + componentWillUnmount() { + log.push(['componentWillUnmount']); + } + render() { + log.push(['render']); + return null; + } + } + + var container = document.createElement('div'); + var inst = ReactDOM.render( + , + container + ); + expect(log).toEqual([ + ['componentWillMount'], + ['render'], + ['componentDidMount'], + ]); + log = []; + + inst.setState({y: 2}); + expect(log).toEqual([ + ['shouldComponentUpdate', {x: 1}, {y: 2}], + ['componentWillUpdate', {x: 1}, {y: 2}], + ['render'], + ['componentDidUpdate', {x: 1}, {y: 1}], + ]); + log = []; + + inst.setState({y: 2}); + expect(log).toEqual([ + ['shouldComponentUpdate', {x: 1}, {y: 2}], + ]); + log = []; + + ReactDOM.render( + , + container + ); + expect(log).toEqual([ + ['componentWillReceiveProps', {x: 2}], + ['shouldComponentUpdate', {x: 2}, {y: 2}], + ['componentWillUpdate', {x: 2}, {y: 2}], + ['render'], + ['componentDidUpdate', {x: 1}, {y: 2}], + ]); + log = []; + + ReactDOM.render( + , + container + ); + expect(log).toEqual([ + ['componentWillReceiveProps', {x: 2}], + ['shouldComponentUpdate', {x: 2}, {y: 2}], + ]); + log = []; + + ReactDOM.unmountComponentAtNode(container); + expect(log).toEqual([ + ['componentWillUnmount'], + ]); + }); + it('should throw with an error code in production', function() { expect(function() { class Component extends React.Component { diff --git a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js index 9cabf27653d12..d0137e73f80d3 100644 --- a/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js +++ b/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js @@ -72,6 +72,13 @@ function isPureComponent(Component) { // Separated into a function to contain deoptimizations caused by try/finally. function measureLifeCyclePerf(fn, debugID, timerType) { + if (debugID === 0) { + // Top-level wrappers (see ReactMount) and empty components (see + // ReactDOMEmptyComponent) are invisible to hooks and devtools. + // Both are implementation details that should go away in the future. + return fn(); + } + ReactInstrumentation.debugTool.onBeginLifeCycleTimer(debugID, timerType); try { return fn(); @@ -338,20 +345,15 @@ var ReactCompositeComponentMixin = { markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context); } - var debugID = 0; - if (__DEV__) { - debugID = this._debugID; - } - if (inst.componentDidMount) { - if (__DEV__ && debugID !== 0) { + if (__DEV__) { transaction.getReactMountReady().enqueue(() => { measureLifeCyclePerf( () => inst.componentDidMount(), - debugID, + this._debugID, 'componentDidMount' ); - }, null); + }); } else { transaction.getReactMountReady().enqueue(inst.componentDidMount, inst); } @@ -396,16 +398,11 @@ var ReactCompositeComponentMixin = { ) { var Component = this._currentElement.type; - var debugID = 0; - if (__DEV__) { - debugID = this._debugID; - } - if (doConstruct) { - if (__DEV__ && debugID !== 0) { + if (__DEV__) { return measureLifeCyclePerf( () => new Component(publicProps, publicContext, updateQueue), - debugID, + this._debugID, 'ctor' ); } else { @@ -415,10 +412,10 @@ var ReactCompositeComponentMixin = { // This can still be an instance in case of factory components // but we'll count this as time spent rendering as the more common case. - if (__DEV__ && debugID !== 0) { + if (__DEV__) { return measureLifeCyclePerf( () => Component(publicProps, publicContext, updateQueue), - debugID, + this._debugID, 'render' ); } else { @@ -465,7 +462,7 @@ var ReactCompositeComponentMixin = { } if (inst.componentWillMount) { - if (__DEV__ && debugID !== 0) { + if (__DEV__) { measureLifeCyclePerf( () => inst.componentWillMount(), debugID, @@ -503,9 +500,11 @@ var ReactCompositeComponentMixin = { debugID ); - if (__DEV__ && debugID !== 0) { - var childDebugIDs = child._debugID !== 0 ? [child._debugID] : []; - ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs); + if (__DEV__) { + if (debugID !== 0) { + var childDebugIDs = child._debugID !== 0 ? [child._debugID] : []; + ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs); + } } return markup; @@ -528,11 +527,6 @@ var ReactCompositeComponentMixin = { var inst = this._instance; - var debugID = 0; - if (__DEV__) { - debugID = this._debugID; - } - if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) { inst._calledComponentWillUnmount = true; @@ -540,10 +534,10 @@ var ReactCompositeComponentMixin = { var name = this.getName() + '.componentWillUnmount()'; ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst)); } else { - if (__DEV__ && debugID !== 0) { + if (__DEV__) { measureLifeCyclePerf( () => inst.componentWillUnmount(), - debugID, + this._debugID, 'componentWillUnmount' ); } else { @@ -793,19 +787,14 @@ var ReactCompositeComponentMixin = { willReceive = true; } - var debugID = 0; - if (__DEV__) { - debugID = this._debugID; - } - // An update here will schedule an update but immediately set // _pendingStateQueue which will ensure that any state updates gets // immediately reconciled instead of waiting for the next batch. if (willReceive && inst.componentWillReceiveProps) { - if (__DEV__ && debugID !== 0) { + if (__DEV__) { measureLifeCyclePerf( () => inst.componentWillReceiveProps(nextProps, nextContext), - debugID, + this._debugID, 'componentWillReceiveProps', ); } else { @@ -818,10 +807,10 @@ var ReactCompositeComponentMixin = { if (!this._pendingForceUpdate) { if (inst.shouldComponentUpdate) { - if (__DEV__ && debugID !== 0) { + if (__DEV__) { shouldUpdate = measureLifeCyclePerf( () => inst.shouldComponentUpdate(nextProps, nextState, nextContext), - debugID, + this._debugID, 'shouldComponentUpdate' ); } else { @@ -929,16 +918,11 @@ var ReactCompositeComponentMixin = { prevContext = inst.context; } - var debugID = 0; - if (__DEV__) { - debugID = this._debugID; - } - if (inst.componentWillUpdate) { - if (__DEV__ && debugID !== 0) { + if (__DEV__) { measureLifeCyclePerf( () => inst.componentWillUpdate(nextProps, nextState, nextContext), - debugID, + this._debugID, 'componentWillUpdate' ); } else { @@ -955,14 +939,14 @@ var ReactCompositeComponentMixin = { this._updateRenderedComponent(transaction, unmaskedContext); if (hasComponentDidUpdate) { - if (__DEV__ && debugID !== 0) { + if (__DEV__) { transaction.getReactMountReady().enqueue(() => { measureLifeCyclePerf( - () => inst.componentDidUpdate(prevProps, prevState, prevContext), - debugID, + inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), + this._debugID, 'componentDidUpdate' ); - }, null); + }); } else { transaction.getReactMountReady().enqueue( inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext), @@ -1016,9 +1000,11 @@ var ReactCompositeComponentMixin = { debugID ); - if (__DEV__ && debugID !== 0) { - var childDebugIDs = child._debugID !== 0 ? [child._debugID] : []; - ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs); + if (__DEV__) { + if (debugID !== 0) { + var childDebugIDs = child._debugID !== 0 ? [child._debugID] : []; + ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs); + } } this._replaceNodeWithMarkup( @@ -1049,15 +1035,10 @@ var ReactCompositeComponentMixin = { var inst = this._instance; var renderedComponent; - var debugID = 0; if (__DEV__) { - debugID = this._debugID; - } - - if (__DEV__ && debugID !== 0) { renderedComponent = measureLifeCyclePerf( () => inst.render(), - debugID, + this._debugID, 'render' ); } else {