@@ -28,6 +28,12 @@ var invariant = require('invariant');
2828var shouldUpdateReactComponent = require ( 'shouldUpdateReactComponent' ) ;
2929var warning = require ( 'warning' ) ;
3030
31+ /**
32+ * Used to indicate that no error has been thrown (since you can actually throw
33+ * null, undefined, and seemingly anything else).
34+ */
35+ var NO_ERROR = { } ;
36+
3137function getDeclarationErrorAddendum ( component ) {
3238 var owner = component . _currentElement . _owner || null ;
3339 if ( owner ) {
@@ -99,16 +105,16 @@ var ReactCompositeComponentMixin = {
99105 this . _instance = null ;
100106 this . _nativeParent = null ;
101107 this . _nativeContainerInfo = null ;
108+ this . _renderedNodeType = null ;
102109
103110 // See ReactUpdateQueue
104111 this . _pendingElement = null ;
105112 this . _pendingStateQueue = null ;
106113 this . _pendingReplaceState = false ;
107114 this . _pendingForceUpdate = false ;
108115
109- this . _renderedNodeType = null ;
116+ this . _caughtError = NO_ERROR ;
110117 this . _renderedComponent = null ;
111-
112118 this . _context = null ;
113119 this . _mountOrder = 0 ;
114120 this . _topLevelWrapper = null ;
@@ -278,6 +284,36 @@ var ReactCompositeComponentMixin = {
278284 this . _pendingReplaceState = false ;
279285 this . _pendingForceUpdate = false ;
280286
287+ var markup ;
288+ if ( inst . handleError ) {
289+ var checkpoint = transaction . checkpoint ( ) ;
290+ try {
291+ markup = this . performInitialMount ( renderedElement , nativeParent , nativeContainerInfo , transaction , context ) ;
292+ } catch ( e ) {
293+ this . _caughtError = e ;
294+ inst . handleError ( e ) ;
295+ this . _renderedComponent . unmountComponent ( ) ;
296+ transaction . rollback ( checkpoint ) ;
297+
298+ // Try again - we've informed the component about the error, so they can render an error message this time.
299+ // If this throws again, the error will bubble up (and can be caught by a higher error boundary).
300+ markup = this . performInitialMount ( renderedElement , nativeParent , nativeContainerInfo , transaction , context ) ;
301+ // Don't call componentDidMount (TODO: wait, what? why not?)
302+ return markup ;
303+ }
304+ } else {
305+ markup = this . performInitialMount ( renderedElement , nativeParent , nativeContainerInfo , transaction , context ) ;
306+ }
307+
308+ if ( inst . componentDidMount ) {
309+ transaction . getReactMountReady ( ) . enqueue ( inst . componentDidMount , inst ) ;
310+ }
311+
312+ return markup ;
313+ } ,
314+
315+ performInitialMount : function ( renderedElement , nativeParent , nativeContainerInfo , transaction , context ) {
316+ var inst = this . _instance ;
281317 if ( inst . componentWillMount ) {
282318 inst . componentWillMount ( ) ;
283319 // When mounting, calls to `setState` by `componentWillMount` will set
@@ -304,9 +340,6 @@ var ReactCompositeComponentMixin = {
304340 nativeContainerInfo ,
305341 this . _processChildContext ( context )
306342 ) ;
307- if ( inst . componentDidMount ) {
308- transaction . getReactMountReady ( ) . enqueue ( inst . componentDidMount , inst ) ;
309- }
310343
311344 return markup ;
312345 } ,
@@ -328,10 +361,12 @@ var ReactCompositeComponentMixin = {
328361 inst . componentWillUnmount ( ) ;
329362 }
330363
331- ReactReconciler . unmountComponent ( this . _renderedComponent ) ;
332- this . _renderedNodeType = null ;
333- this . _renderedComponent = null ;
334- this . _instance = null ;
364+ if ( this . _renderedComponent ) {
365+ ReactReconciler . unmountComponent ( this . _renderedComponent ) ;
366+ this . _renderedNodeType = null ;
367+ this . _renderedComponent = null ;
368+ this . _instance = null ;
369+ }
335370
336371 // Reset pending fields
337372 // Even if this component is scheduled for another update in ReactUpdates,
@@ -786,7 +821,8 @@ var ReactCompositeComponentMixin = {
786821 */
787822 _renderValidatedComponentWithoutOwnerOrContext : function ( ) {
788823 var inst = this . _instance ;
789- var renderedComponent = inst . render ( ) ;
824+ var renderedComponent ;
825+ renderedComponent = inst . render ( ) ;
790826 if ( __DEV__ ) {
791827 // We allow auto-mocks to proceed as if they're returning null.
792828 if ( typeof renderedComponent === 'undefined' &&
0 commit comments