diff --git a/Libraries/CustomComponents/Navigator/Navigator.js b/Libraries/CustomComponents/Navigator/Navigator.js index 5f2fcbfa6..991c97021 100644 --- a/Libraries/CustomComponents/Navigator/Navigator.js +++ b/Libraries/CustomComponents/Navigator/Navigator.js @@ -71,7 +71,7 @@ var styles = StyleSheet.create({ bottom: 0, top: 0, }, - presentNavItem: { + currentScene: { position: 'absolute', overflow: 'hidden', left: 0, @@ -79,7 +79,7 @@ var styles = StyleSheet.create({ bottom: 0, top: 0, }, - futureNavItem: { + futureScene: { overflow: 'hidden', position: 'absolute', left: 0, @@ -991,9 +991,42 @@ var Navigator = React.createClass({ } }, - _routeToOptimizedStackItem: function(route, i) { - var shouldUpdateChild = - this.state.updatingRangeLength !== 0 && + _renderOptimizedScenes: function() { + // To avoid rendering scenes that are not visible, we use + // updatingRangeStart and updatingRangeLength to track the scenes that need + // to be updated. + + // To avoid visual glitches, we never re-render scenes during a transition. + // We assume that `state.updatingRangeLength` will have a length during the + // initial render of any scene + var shouldRenderScenes = !this.state.isAnimating && + this.state.updatingRangeLength !== 0; + if (shouldRenderScenes) { + return ( + + + {this.state.routeStack.map(this._renderOptimizedScene)} + + + ); + } + // If no scenes are changing, we can save render time. React will notice + // that we are rendering a StaticContainer in the same place, so the + // existing element will be updated. When React asks the element + // shouldComponentUpdate, the StaticContainer will return false, and the + // children from the previous reconciliation will remain. + return ( + + ); + }, + + _renderOptimizedScene: function(route, i) { + var shouldRenderScene = i >= this.state.updatingRangeStart && i <= this.state.updatingRangeStart + this.state.updatingRangeLength; var sceneNavigatorContext = { @@ -1003,49 +1036,38 @@ var Navigator = React.createClass({ this.navigatorContext.setHandlerForRoute(route, handler); }, }; - var child = this.props.renderScene( - route, - sceneNavigatorContext - ); - var initialSceneStyle = - i === this.state.presentedIndex ? styles.presentNavItem : styles.futureNavItem; + var scene = shouldRenderScene ? + this._renderScene(route, i, sceneNavigatorContext) : null; return ( - - {React.cloneElement(child, { - ref: this._handleItemRef.bind(null, this.state.idStack[i]), - })} - + shouldUpdate={shouldRenderScene}> + {scene} ); }, - renderNavigationStackItems: function() { - var shouldRecurseToNavigator = this.state.updatingRangeLength !== 0; - // If not recursing update to navigator at all, may as well avoid - // computation of navigator children. - var items = shouldRecurseToNavigator ? - this.state.routeStack.map(this._routeToOptimizedStackItem) : null; - + _renderScene: function(route, i, sceneNavigatorContext) { + var child = this.props.renderScene( + route, + sceneNavigatorContext + ); + var initialSceneStyle = i === this.state.presentedIndex ? + styles.currentScene : styles.futureScene; return ( - - - {items} - - + + {React.cloneElement(child, { + ref: this._handleItemRef.bind(null, this.state.idStack[i]), + })} + ); }, - renderNavigationStackBar: function() { + _renderNavigationBar: function() { if (!this.props.navigationBar) { return null; } @@ -1059,8 +1081,8 @@ var Navigator = React.createClass({ render: function() { return ( - {this.renderNavigationStackItems()} - {this.renderNavigationStackBar()} + {this._renderOptimizedScenes()} + {this._renderNavigationBar()} ); },