@samitouri / QOS-React / commits / a229cdba7f

Ensure lifecycle timers are stopped on errors (#7548)

* Ensure lifecycle timers are stopped on errors Fixes #7349 * Address review feedback and add an extra test

Dan Abramov committed Aug 24, 2016 at 19:18 UTC a229cdba7fd00799d82c1bab23704220d47e6331
4 files changed +419 -200
src/renderers/dom/__tests__/ReactDOMProduction-test.js
+88
@@ -86,6 +86,94 @@ describe('ReactDOMProduction', function() {
86 expect(container.childNodes.length).toBe(0);
87 });
88
89 + it('should call lifecycle methods', function() {
90 + var log = [];
91 + class Component extends React.Component {
92 + state = {y: 1};
93 + shouldComponentUpdate(nextProps, nextState) {
94 + log.push(['shouldComponentUpdate', nextProps, nextState]);
95 + return nextProps.x !== this.props.x || nextState.y !== this.state.y;
96 + }
97 + componentWillMount() {
98 + log.push(['componentWillMount']);
99 + }
100 + componentDidMount() {
101 + log.push(['componentDidMount']);
102 + }
103 + componentWillReceiveProps(nextProps) {
104 + log.push(['componentWillReceiveProps', nextProps]);
105 + }
106 + componentWillUpdate(nextProps, nextState) {
107 + log.push(['componentWillUpdate', nextProps, nextState]);
108 + }
109 + componentDidUpdate(prevProps, prevState) {
110 + log.push(['componentDidUpdate', prevProps, prevState]);
111 + }
112 + componentWillUnmount() {
113 + log.push(['componentWillUnmount']);
114 + }
115 + render() {
116 + log.push(['render']);
117 + return null;
118 + }
119 + }
120 +
121 + var container = document.createElement('div');
122 + var inst = ReactDOM.render(
123 + <Component x={1} />,
124 + container
125 + );
126 + expect(log).toEqual([
127 + ['componentWillMount'],
128 + ['render'],
129 + ['componentDidMount'],
130 + ]);
131 + log = [];
132 +
133 + inst.setState({y: 2});
134 + expect(log).toEqual([
135 + ['shouldComponentUpdate', {x: 1}, {y: 2}],
136 + ['componentWillUpdate', {x: 1}, {y: 2}],
137 + ['render'],
138 + ['componentDidUpdate', {x: 1}, {y: 1}],
139 + ]);
140 + log = [];
141 +
142 + inst.setState({y: 2});
143 + expect(log).toEqual([
144 + ['shouldComponentUpdate', {x: 1}, {y: 2}],
145 + ]);
146 + log = [];
147 +
148 + ReactDOM.render(
149 + <Component x={2} />,
150 + container
151 + );
152 + expect(log).toEqual([
153 + ['componentWillReceiveProps', {x: 2}],
154 + ['shouldComponentUpdate', {x: 2}, {y: 2}],
155 + ['componentWillUpdate', {x: 2}, {y: 2}],
156 + ['render'],
157 + ['componentDidUpdate', {x: 1}, {y: 2}],
158 + ]);
159 + log = [];
160 +
161 + ReactDOM.render(
162 + <Component x={2} />,
163 + container
164 + );
165 + expect(log).toEqual([
166 + ['componentWillReceiveProps', {x: 2}],
167 + ['shouldComponentUpdate', {x: 2}, {y: 2}],
168 + ]);
169 + log = [];
170 +
171 + ReactDOM.unmountComponentAtNode(container);
172 + expect(log).toEqual([
173 + ['componentWillUnmount'],
174 + ]);
175 + });
176 +
177 it('should throw with an error code in production', function() {
178 expect(function() {
179 class Component extends React.Component {
src/renderers/shared/ReactDebugTool.js
-6
@@ -310,12 +310,6 @@ var ReactDebugTool = {
310 markEnd(debugID, timerType);
311 emitEvent('onEndLifeCycleTimer', debugID, timerType);
312 },
313 - onError(debugID) {
314 - if (currentTimerDebugID != null) {
315 - endLifeCycleTimer(currentTimerDebugID, currentTimerType);
316 - }
317 - emitEvent('onError', debugID);
318 - },
313 onBeginProcessingChildContext() {
314 emitEvent('onBeginProcessingChildContext');
315 },
src/renderers/shared/__tests__/ReactPerf-test.js
+204
@@ -516,4 +516,208 @@ describe('ReactPerf', function() {
516 renderCount: 2,
517 }]);
518 });
519 +
520 + it('should not print errant warnings if render() throws', () => {
521 + var container = document.createElement('div');
522 + var thrownErr = new Error('Muhaha!');
523 +
524 + class Evil extends React.Component {
525 + render() {
526 + throw thrownErr;
527 + }
528 + }
529 +
530 + ReactPerf.start();
531 + try {
532 + ReactDOM.render(
533 + <div>
534 + <LifeCycle />
535 + <Evil />
536 + </div>,
537 + container
538 + );
539 + } catch (err) {
540 + if (err !== thrownErr) {
541 + throw err;
542 + }
543 + }
544 + ReactPerf.stop();
545 + });
546 +
547 + it('should not print errant warnings if componentWillMount() throws', () => {
548 + var container = document.createElement('div');
549 + var thrownErr = new Error('Muhaha!');
550 +
551 + class Evil extends React.Component {
552 + componentWillMount() {
553 + throw thrownErr;
554 + }
555 + render() {
556 + return <div />;
557 + }
558 + }
559 +
560 + ReactPerf.start();
561 + try {
562 + ReactDOM.render(
563 + <div>
564 + <LifeCycle />
565 + <Evil />
566 + </div>,
567 + container
568 + );
569 + } catch (err) {
570 + if (err !== thrownErr) {
571 + throw err;
572 + }
573 + }
574 + ReactPerf.stop();
575 + });
576 +
577 + it('should not print errant warnings if componentDidMount() throws', () => {
578 + var container = document.createElement('div');
579 + var thrownErr = new Error('Muhaha!');
580 +
581 + class Evil extends React.Component {
582 + componentDidMount() {
583 + throw thrownErr;
584 + }
585 + render() {
586 + return <div />;
587 + }
588 + }
589 +
590 + ReactPerf.start();
591 + try {
592 + ReactDOM.render(
593 + <div>
594 + <LifeCycle />
595 + <Evil />
596 + </div>,
597 + container
598 + );
599 + } catch (err) {
600 + if (err !== thrownErr) {
601 + throw err;
602 + }
603 + }
604 + ReactPerf.stop();
605 + });
606 +
607 + it('should not print errant warnings if portal throws in render()', () => {
608 + var container = document.createElement('div');
609 + var thrownErr = new Error('Muhaha!');
610 +
611 + class Evil extends React.Component {
612 + render() {
613 + throw thrownErr;
614 + }
615 + }
616 + class EvilPortal extends React.Component {
617 + componentWillMount() {
618 + var portalContainer = document.createElement('div');
619 + ReactDOM.render(<Evil />, portalContainer);
620 + }
621 + render() {
622 + return <div />;
623 + }
624 + }
625 +
626 + ReactPerf.start();
627 + try {
628 + ReactDOM.render(
629 + <div>
630 + <LifeCycle />
631 + <EvilPortal />
632 + </div>,
633 + container
634 + );
635 + } catch (err) {
636 + if (err !== thrownErr) {
637 + throw err;
638 + }
639 + }
640 + ReactDOM.unmountComponentAtNode(container);
641 + ReactPerf.stop();
642 + });
643 +
644 + it('should not print errant warnings if portal throws in componentWillMount()', () => {
645 + var container = document.createElement('div');
646 + var thrownErr = new Error('Muhaha!');
647 +
648 + class Evil extends React.Component {
649 + componentWillMount() {
650 + throw thrownErr;
651 + }
652 + render() {
653 + return <div />;
654 + }
655 + }
656 + class EvilPortal extends React.Component {
657 + componentWillMount() {
658 + var portalContainer = document.createElement('div');
659 + ReactDOM.render(<Evil />, portalContainer);
660 + }
661 + render() {
662 + return <div />;
663 + }
664 + }
665 +
666 + ReactPerf.start();
667 + try {
668 + ReactDOM.render(
669 + <div>
670 + <LifeCycle />
671 + <EvilPortal />
672 + </div>,
673 + container
674 + );
675 + } catch (err) {
676 + if (err !== thrownErr) {
677 + throw err;
678 + }
679 + }
680 + ReactDOM.unmountComponentAtNode(container);
681 + ReactPerf.stop();
682 + });
683 +
684 + it('should not print errant warnings if portal throws in componentDidMount()', () => {
685 + var container = document.createElement('div');
686 + var thrownErr = new Error('Muhaha!');
687 +
688 + class Evil extends React.Component {
689 + componentDidMount() {
690 + throw thrownErr;
691 + }
692 + render() {
693 + return <div />;
694 + }
695 + }
696 + class EvilPortal extends React.Component {
697 + componentWillMount() {
698 + var portalContainer = document.createElement('div');
699 + ReactDOM.render(<Evil />, portalContainer);
700 + }
701 + render() {
702 + return <div />;
703 + }
704 + }
705 +
706 + ReactPerf.start();
707 + try {
708 + ReactDOM.render(
709 + <div>
710 + <LifeCycle />
711 + <EvilPortal />
712 + </div>,
713 + container
714 + );
715 + } catch (err) {
716 + if (err !== thrownErr) {
717 + throw err;
718 + }
719 + }
720 + ReactDOM.unmountComponentAtNode(container);
721 + ReactPerf.stop();
722 + });
723 });
src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
+127 -194
@@ -62,40 +62,6 @@ function warnIfInvalidElement(Component, element) {
62 }
63 }
64
65 -function invokeComponentDidMountWithTimer() {
66 - var publicInstance = this._instance;
67 - if (this._debugID !== 0) {
68 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
69 - this._debugID,
70 - 'componentDidMount'
71 - );
72 - }
73 - publicInstance.componentDidMount();
74 - if (this._debugID !== 0) {
75 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
76 - this._debugID,
77 - 'componentDidMount'
78 - );
79 - }
80 -}
81 -
82 -function invokeComponentDidUpdateWithTimer(prevProps, prevState, prevContext) {
83 - var publicInstance = this._instance;
84 - if (this._debugID !== 0) {
85 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
86 - this._debugID,
87 - 'componentDidUpdate'
88 - );
89 - }
90 - publicInstance.componentDidUpdate(prevProps, prevState, prevContext);
91 - if (this._debugID !== 0) {
92 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
93 - this._debugID,
94 - 'componentDidUpdate'
95 - );
96 - }
97 -}
98 -
65 function shouldConstruct(Component) {
66 return !!(Component.prototype && Component.prototype.isReactComponent);
67 }
@@ -104,6 +70,23 @@ function isPureComponent(Component) {
70 return !!(Component.prototype && Component.prototype.isPureReactComponent);
71 }
72
73 +// Separated into a function to contain deoptimizations caused by try/finally.
74 +function measureLifeCyclePerf(fn, debugID, timerType) {
75 + if (debugID === 0) {
76 + // Top-level wrappers (see ReactMount) and empty components (see
77 + // ReactDOMEmptyComponent) are invisible to hooks and devtools.
78 + // Both are implementation details that should go away in the future.
79 + return fn();
80 + }
81 +
82 + ReactInstrumentation.debugTool.onBeginLifeCycleTimer(debugID, timerType);
83 + try {
84 + return fn();
85 + } finally {
86 + ReactInstrumentation.debugTool.onEndLifeCycleTimer(debugID, timerType);
87 + }
88 +}
89 +
90 /**
91 * ------------------ The Life-Cycle of a Composite Component ------------------
92 *
@@ -364,7 +347,13 @@ var ReactCompositeComponentMixin = {
347
348 if (inst.componentDidMount) {
349 if (__DEV__) {
367 - transaction.getReactMountReady().enqueue(invokeComponentDidMountWithTimer, this);
350 + transaction.getReactMountReady().enqueue(() => {
351 + measureLifeCyclePerf(
352 + () => inst.componentDidMount(),
353 + this._debugID,
354 + 'componentDidMount'
355 + );
356 + });
357 } else {
358 transaction.getReactMountReady().enqueue(inst.componentDidMount, inst);
359 }
@@ -408,47 +397,30 @@ var ReactCompositeComponentMixin = {
397 updateQueue
398 ) {
399 var Component = this._currentElement.type;
411 - var instanceOrElement;
400 +
401 if (doConstruct) {
402 if (__DEV__) {
414 - if (this._debugID !== 0) {
415 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
416 - this._debugID,
417 - 'ctor'
418 - );
419 - }
420 - }
421 - instanceOrElement = new Component(publicProps, publicContext, updateQueue);
422 - if (__DEV__) {
423 - if (this._debugID !== 0) {
424 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
425 - this._debugID,
426 - 'ctor'
427 - );
428 - }
403 + return measureLifeCyclePerf(
404 + () => new Component(publicProps, publicContext, updateQueue),
405 + this._debugID,
406 + 'ctor'
407 + );
408 + } else {
409 + return new Component(publicProps, publicContext, updateQueue);
410 }
411 + }
412 +
413 + // This can still be an instance in case of factory components
414 + // but we'll count this as time spent rendering as the more common case.
415 + if (__DEV__) {
416 + return measureLifeCyclePerf(
417 + () => Component(publicProps, publicContext, updateQueue),
418 + this._debugID,
419 + 'render'
420 + );
421 } else {
431 - // This can still be an instance in case of factory components
432 - // but we'll count this as time spent rendering as the more common case.
433 - if (__DEV__) {
434 - if (this._debugID !== 0) {
435 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
436 - this._debugID,
437 - 'render'
438 - );
439 - }
440 - }
441 - instanceOrElement = Component(publicProps, publicContext, updateQueue);
442 - if (__DEV__) {
443 - if (this._debugID !== 0) {
444 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
445 - this._debugID,
446 - 'render'
447 - );
448 - }
449 - }
422 + return Component(publicProps, publicContext, updateQueue);
423 }
451 - return instanceOrElement;
424 },
425
426 performInitialMountWithErrorHandling: function(
@@ -463,11 +435,6 @@ var ReactCompositeComponentMixin = {
435 try {
436 markup = this.performInitialMount(renderedElement, hostParent, hostContainerInfo, transaction, context);
437 } catch (e) {
466 - if (__DEV__) {
467 - if (this._debugID !== 0) {
468 - ReactInstrumentation.debugTool.onError();
469 - }
470 - }
438 // Roll back to checkpoint, handle error (which may add items to the transaction), and take a new checkpoint
439 transaction.rollback(checkpoint);
440 this._instance.unstable_handleError(e);
@@ -488,23 +455,21 @@ var ReactCompositeComponentMixin = {
455
456 performInitialMount: function(renderedElement, hostParent, hostContainerInfo, transaction, context) {
457 var inst = this._instance;
458 +
459 + var debugID = 0;
460 + if (__DEV__) {
461 + debugID = this._debugID;
462 + }
463 +
464 if (inst.componentWillMount) {
465 if (__DEV__) {
493 - if (this._debugID !== 0) {
494 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
495 - this._debugID,
496 - 'componentWillMount'
497 - );
498 - }
499 - }
500 - inst.componentWillMount();
501 - if (__DEV__) {
502 - if (this._debugID !== 0) {
503 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
504 - this._debugID,
505 - 'componentWillMount'
506 - );
507 - }
466 + measureLifeCyclePerf(
467 + () => inst.componentWillMount(),
468 + debugID,
469 + 'componentWillMount'
470 + );
471 + } else {
472 + inst.componentWillMount();
473 }
474 // When mounting, calls to `setState` by `componentWillMount` will set
475 // `this._pendingStateQueue` without triggering a re-render.
@@ -526,25 +491,19 @@ var ReactCompositeComponentMixin = {
491 );
492 this._renderedComponent = child;
493
529 - var selfDebugID = 0;
530 - if (__DEV__) {
531 - selfDebugID = this._debugID;
532 - }
494 var markup = ReactReconciler.mountComponent(
495 child,
496 transaction,
497 hostParent,
498 hostContainerInfo,
499 this._processChildContext(context),
539 - selfDebugID
500 + debugID
501 );
502
503 if (__DEV__) {
543 - if (this._debugID !== 0) {
544 - ReactInstrumentation.debugTool.onSetChildren(
545 - this._debugID,
546 - child._debugID !== 0 ? [child._debugID] : []
547 - );
504 + if (debugID !== 0) {
505 + var childDebugIDs = child._debugID !== 0 ? [child._debugID] : [];
506 + ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs);
507 }
508 }
509
@@ -565,30 +524,24 @@ var ReactCompositeComponentMixin = {
524 if (!this._renderedComponent) {
525 return;
526 }
527 +
528 var inst = this._instance;
529
530 if (inst.componentWillUnmount && !inst._calledComponentWillUnmount) {
531 inst._calledComponentWillUnmount = true;
572 - if (__DEV__) {
573 - if (this._debugID !== 0) {
574 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
575 - this._debugID,
576 - 'componentWillUnmount'
577 - );
578 - }
579 - }
532 +
533 if (safely) {
534 var name = this.getName() + '.componentWillUnmount()';
535 ReactErrorUtils.invokeGuardedCallback(name, inst.componentWillUnmount.bind(inst));
536 } else {
584 - inst.componentWillUnmount();
585 - }
586 - if (__DEV__) {
587 - if (this._debugID !== 0) {
588 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
537 + if (__DEV__) {
538 + measureLifeCyclePerf(
539 + () => inst.componentWillUnmount(),
540 this._debugID,
541 'componentWillUnmount'
542 );
543 + } else {
544 + inst.componentWillUnmount();
545 }
546 }
547 }
@@ -679,13 +632,21 @@ var ReactCompositeComponentMixin = {
632 _processChildContext: function(currentContext) {
633 var Component = this._currentElement.type;
634 var inst = this._instance;
682 - if (__DEV__) {
683 - ReactInstrumentation.debugTool.onBeginProcessingChildContext();
684 - }
685 - var childContext = inst.getChildContext && inst.getChildContext();
686 - if (__DEV__) {
687 - ReactInstrumentation.debugTool.onEndProcessingChildContext();
635 + var childContext;
636 +
637 + if (inst.getChildContext) {
638 + if (__DEV__) {
639 + ReactInstrumentation.debugTool.onBeginProcessingChildContext();
640 + try {
641 + childContext = inst.getChildContext();
642 + } finally {
643 + ReactInstrumentation.debugTool.onEndProcessingChildContext();
644 + }
645 + } else {
646 + childContext = inst.getChildContext();
647 + }
648 }
649 +
650 if (childContext) {
651 invariant(
652 typeof Component.childContextTypes === 'object',
@@ -831,21 +792,13 @@ var ReactCompositeComponentMixin = {
792 // immediately reconciled instead of waiting for the next batch.
793 if (willReceive && inst.componentWillReceiveProps) {
794 if (__DEV__) {
834 - if (this._debugID !== 0) {
835 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
836 - this._debugID,
837 - 'componentWillReceiveProps'
838 - );
839 - }
840 - }
841 - inst.componentWillReceiveProps(nextProps, nextContext);
842 - if (__DEV__) {
843 - if (this._debugID !== 0) {
844 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
845 - this._debugID,
846 - 'componentWillReceiveProps'
847 - );
848 - }
795 + measureLifeCyclePerf(
796 + () => inst.componentWillReceiveProps(nextProps, nextContext),
797 + this._debugID,
798 + 'componentWillReceiveProps',
799 + );
800 + } else {
801 + inst.componentWillReceiveProps(nextProps, nextContext);
802 }
803 }
804
@@ -855,21 +808,13 @@ var ReactCompositeComponentMixin = {
808 if (!this._pendingForceUpdate) {
809 if (inst.shouldComponentUpdate) {
810 if (__DEV__) {
858 - if (this._debugID !== 0) {
859 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
860 - this._debugID,
861 - 'shouldComponentUpdate'
862 - );
863 - }
864 - }
865 - shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
866 - if (__DEV__) {
867 - if (this._debugID !== 0) {
868 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
869 - this._debugID,
870 - 'shouldComponentUpdate'
871 - );
872 - }
811 + shouldUpdate = measureLifeCyclePerf(
812 + () => inst.shouldComponentUpdate(nextProps, nextState, nextContext),
813 + this._debugID,
814 + 'shouldComponentUpdate'
815 + );
816 + } else {
817 + shouldUpdate = inst.shouldComponentUpdate(nextProps, nextState, nextContext);
818 }
819 } else {
820 if (this._compositeType === CompositeTypes.PureClass) {
@@ -975,21 +920,13 @@ var ReactCompositeComponentMixin = {
920
921 if (inst.componentWillUpdate) {
922 if (__DEV__) {
978 - if (this._debugID !== 0) {
979 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
980 - this._debugID,
981 - 'componentWillUpdate'
982 - );
983 - }
984 - }
985 - inst.componentWillUpdate(nextProps, nextState, nextContext);
986 - if (__DEV__) {
987 - if (this._debugID !== 0) {
988 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
989 - this._debugID,
990 - 'componentWillUpdate'
991 - );
992 - }
923 + measureLifeCyclePerf(
924 + () => inst.componentWillUpdate(nextProps, nextState, nextContext),
925 + this._debugID,
926 + 'componentWillUpdate'
927 + );
928 + } else {
929 + inst.componentWillUpdate(nextProps, nextState, nextContext);
930 }
931 }
932
@@ -1003,10 +940,13 @@ var ReactCompositeComponentMixin = {
940
941 if (hasComponentDidUpdate) {
942 if (__DEV__) {
1006 - transaction.getReactMountReady().enqueue(
1007 - invokeComponentDidUpdateWithTimer.bind(this, prevProps, prevState, prevContext),
1008 - this
1009 - );
943 + transaction.getReactMountReady().enqueue(() => {
944 + measureLifeCyclePerf(
945 + inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
946 + this._debugID,
947 + 'componentDidUpdate'
948 + );
949 + });
950 } else {
951 transaction.getReactMountReady().enqueue(
952 inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
@@ -1026,6 +966,12 @@ var ReactCompositeComponentMixin = {
966 var prevComponentInstance = this._renderedComponent;
967 var prevRenderedElement = prevComponentInstance._currentElement;
968 var nextRenderedElement = this._renderValidatedComponent();
969 +
970 + var debugID = 0;
971 + if (__DEV__) {
972 + debugID = this._debugID;
973 + }
974 +
975 if (shouldUpdateReactComponent(prevRenderedElement, nextRenderedElement)) {
976 ReactReconciler.receiveComponent(
977 prevComponentInstance,
@@ -1045,25 +991,19 @@ var ReactCompositeComponentMixin = {
991 );
992 this._renderedComponent = child;
993
1048 - var selfDebugID = 0;
1049 - if (__DEV__) {
1050 - selfDebugID = this._debugID;
1051 - }
994 var nextMarkup = ReactReconciler.mountComponent(
995 child,
996 transaction,
997 this._hostParent,
998 this._hostContainerInfo,
999 this._processChildContext(context),
1058 - selfDebugID
1000 + debugID
1001 );
1002
1003 if (__DEV__) {
1062 - if (this._debugID !== 0) {
1063 - ReactInstrumentation.debugTool.onSetChildren(
1064 - this._debugID,
1065 - child._debugID !== 0 ? [child._debugID] : []
1066 - );
1004 + if (debugID !== 0) {
1005 + var childDebugIDs = child._debugID !== 0 ? [child._debugID] : [];
1006 + ReactInstrumentation.debugTool.onSetChildren(debugID, childDebugIDs);
1007 }
1008 }
1009
@@ -1093,23 +1033,16 @@ var ReactCompositeComponentMixin = {
1033 */
1034 _renderValidatedComponentWithoutOwnerOrContext: function() {
1035 var inst = this._instance;
1036 + var renderedComponent;
1037
1038 if (__DEV__) {
1098 - if (this._debugID !== 0) {
1099 - ReactInstrumentation.debugTool.onBeginLifeCycleTimer(
1100 - this._debugID,
1101 - 'render'
1102 - );
1103 - }
1104 - }
1105 - var renderedComponent = inst.render();
1106 - if (__DEV__) {
1107 - if (this._debugID !== 0) {
1108 - ReactInstrumentation.debugTool.onEndLifeCycleTimer(
1109 - this._debugID,
1110 - 'render'
1111 - );
1112 - }
1039 + renderedComponent = measureLifeCyclePerf(
1040 + () => inst.render(),
1041 + this._debugID,
1042 + 'render'
1043 + );
1044 + } else {
1045 + renderedComponent = inst.render();
1046 }
1047
1048 if (__DEV__) {