From 68918e8773d8f14f6d8203a5a3af05d9a4b14672 Mon Sep 17 00:00:00 2001 From: Hedger Wang Date: Tue, 29 Mar 2016 15:56:53 -0700 Subject: [PATCH] Clean up NavigationView. Summary:This address the issue reported at https://github.com/facebook/react-native/issues/6579#issuecomment-200984628 NavigationView should have the same API as NavigationAnimatedView does except that NavigationView does not need the APIs for animation. This unify the API of our core components so that people can freely compose views with both NavigationAnimatedView or NavigationView. Reviewed By: fkgozali Differential Revision: D3096076 fb-gh-sync-id: 7536777a7d637da62a2636d750f9d91c5a0eb45f fbshipit-source-id: 7536777a7d637da62a2636d750f9d91c5a0eb45f --- .../NavigationCompositionExample.js | 27 ++- .../NavigationAnimatedView.js | 3 +- .../NavigationExperimental/NavigationView.js | 181 ++++++++++++++---- 3 files changed, 169 insertions(+), 42 deletions(-) diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js index d24462648..cdf78b17a 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js @@ -265,21 +265,33 @@ class NavigationCompositionExample extends React.Component { } class ExampleMainView extends React.Component { + _renderScene: NavigationSceneRenderer; + + componentWillMount() { + this._renderScene = this._renderScene.bind(this); + } + render() { return ( ( - - )} + renderScene={this._renderScene} /> ); } + + _renderScene(props: NavigationSceneRendererProps): ReactElement { + const {scene} = props; + return ( + + ); + } + _handleNavigation(tabKey, action) { if (ExampleExitAction.match(action)) { this.props.onExampleExit(); @@ -288,6 +300,7 @@ class ExampleMainView extends React.Component { this.props.onNavigate(action); } } + ExampleMainView = NavigationContainer.create(ExampleMainView); const styles = StyleSheet.create({ diff --git a/Libraries/NavigationExperimental/NavigationAnimatedView.js b/Libraries/NavigationExperimental/NavigationAnimatedView.js index ac5293373..e2635e267 100644 --- a/Libraries/NavigationExperimental/NavigationAnimatedView.js +++ b/Libraries/NavigationExperimental/NavigationAnimatedView.js @@ -20,6 +20,7 @@ const StyleSheet = require('StyleSheet'); const View = require('View'); import type { + NavigationActionCaller, NavigationAnimatedValue, NavigationAnimationSetter, NavigationLayout, @@ -31,7 +32,7 @@ import type { type Props = { applyAnimation: NavigationAnimationSetter, navigationState: NavigationParentState, - onNavigate: (action: any) => void, + onNavigate: NavigationActionCaller, renderOverlay: ?NavigationSceneRenderer, renderScene: NavigationSceneRenderer, style: any, diff --git a/Libraries/NavigationExperimental/NavigationView.js b/Libraries/NavigationExperimental/NavigationView.js index 5789cfbf3..c79466d7a 100644 --- a/Libraries/NavigationExperimental/NavigationView.js +++ b/Libraries/NavigationExperimental/NavigationView.js @@ -11,51 +11,164 @@ */ 'use strict'; -var React = require('React'); -var NavigationContainer = require('NavigationContainer'); -var StyleSheet = require('StyleSheet'); -var View = require('View'); +const Animated = require('Animated'); +const NavigationContainer = require('NavigationContainer'); +const React = require('react-native'); +const StyleSheet = require('StyleSheet'); +const View = require('View'); +const NavigationScenesReducer = require('NavigationScenesReducer'); +const ReactComponentWithPureRenderMixin = require('ReactComponentWithPureRenderMixin'); + +import type { + NavigationActionCaller, + NavigationAnimatedValue, + NavigationLayout, + NavigationParentState, + NavigationScene, + NavigationSceneRenderer, + NavigationSceneRendererProps, +} from 'NavigationTypeDefinition'; + +type Props = { + navigationState: NavigationParentState, + onNavigate: NavigationActionCaller, + renderScene: NavigationSceneRenderer, + style: any, +}; + +type State = { + scenes: Array, +}; + +const {PropTypes} = React; + +/** + * A simple view that will render a scene for the currently focused sub-state. + * The most common use-case is for tabs, where no transition is needed + */ +class NavigationView extends React.Component { + _layout: NavigationLayout; + _onLayout: (event: any) => void; + _position: NavigationAnimatedValue; + + props: Props; + state: State; + + static propTypes = { + navigationState: PropTypes.object.isRequired, + onNavigate: PropTypes.func.isRequired, + renderScene: PropTypes.func.isRequired, + }; + + constructor(props: Props, context: any) { + super(props, context); + + this._layout = { + initWidth: 0, + initHeight: 0, + width: new Animated.Value(0), + height: new Animated.Value(0), + }; + + const {navigationState} = this.props; + + this._position = new Animated.Value(navigationState.index); + + this.state = { + scenes: NavigationScenesReducer([], navigationState), + }; + } + + shouldComponentUpdate(nextProps: Props, nextState: State): boolean { + return ReactComponentWithPureRenderMixin.shouldComponentUpdate.call( + this, + nextProps, + nextState + ); + } + + componentWillReceiveProps(nextProps: Props): void { + if (nextProps.navigationState !== this.props.navigationState) { + const {navigationState} = nextProps; + this.setState( + { + scenes: NavigationScenesReducer( + this.state.scenes, + navigationState, + null, // There will be no transtion. + ), + }, + () => { + this._position.setValue(navigationState.index); + }, + ); + } + } + + componentWillMount(): void { + this._onLayout = this._onLayout.bind(this); + } + + render(): ReactElement { + const { + navigationState, + onNavigate + } = this.props; + + const { + scenes, + } = this.state; + + const sceneProps = { + layout: this._layout, + navigationState: navigationState, + onNavigate: onNavigate, + position: this._position, + scene: scenes[navigationState.index], + scenes, + }; -var NavigationView = React.createClass({ - propTypes: { - // todo, figure out a propType for getK - navigationState: React.PropTypes.object.isRequired, - renderScene: React.PropTypes.func.isRequired, - }, - render: function() { return ( - {this.props.navigationState.children.map(this._renderScene)} + {this._renderScene(sceneProps)} ); - }, - _renderScene: function(route, index) { - var isSelected = index === this.props.navigationState.index; - return ( - - {this.props.renderScene(route, index)} - - ); - }, -}); + } -NavigationView = NavigationContainer.create(NavigationView); + _renderScene(props: NavigationSceneRendererProps): ?ReactElement { -var styles = StyleSheet.create({ - navView: { - position: 'absolute', + const child = this.props.renderScene(props); + if (child === null) { + return null; + } + return {child}; + } + + _onLayout(event: any): void { + const {height, width} = event.nativeEvent.layout; + + const layout = { + ...this._layout, + initHeight: height, + initWidth: width, + }; + + this._layout = layout; + layout.height.setValue(height); + layout.width.setValue(width); + } +} + +const styles = StyleSheet.create({ + scene: { + bottom: 0, left: 0, + position: 'absolute', right: 0, top: 0, - bottom: 0, }, }); -module.exports = NavigationView; +module.exports = NavigationContainer.create(NavigationView);