對於這個程序的頁面導航結構,我是這樣看的。首先,一個標籤欄自己就是本身的導航器,而上面每一個標籤一樣是本身的導航器。在這個例子裏,我要用帶有三個標籤的標籤欄,因此一共有四個導航器,每一個導航器都有本身的還原器(reducer)和狀態。html
我又把代碼分紅幾個「功能」,因此整個結構看起來是這樣的:react
/app /tabBar /views TabBarNavigation.js navigationConfiguration.js
/tabOne /views TabOneNavigation.js TabOneScreenOne.js TabOneScreenTwo.js navigationConfigutation.js
/tabTwo /views TabTwoNavigation.js TabTwoScreenOne.js TabTwoScreenTwo.js navigationConfiguration.js
/tabThree /views TabThreeNavigation.js TabThreeScreenOne.js TabThreeScreenTwo.js navigationConfiguration.js
store.js
先從標籤欄開始,標籤欄是程序的入口處,並且是最頂端的導航器。根聽說明文檔,要構建一個標籤欄須要調用一個函數叫TabNavigator(RouteConfigs, TabNavigatorConfig)
。這個函數有兩個參數:RouteConfigs和TabNavigatorConfig,路線配置參數(RouteConfigs
)就是單個的標籤,也表明了單個的導航器。也就是說,這裏每一個單獨的標籤導航器都是咱們給標籤欄設定的導航路線。ios
導航路線以鍵/值成對定義,好比ScreenName: { screen: ScreenName }
,因此路線配置參數就是一列可能要導航的路線。在這個例子裏,導航路線是可能要轉到的標籤。redux
標籤導航設置(TabNavigatorConfig
)參數是程序接口提供的選項,能夠用來自定義標籤欄。這個參數只是由幾對鍵: 值
結構組成的JS對象而已。其中最重要的一對是標籤欄選項(tabBarOptions
),靠這個選項能夠設定標籤激活時與不激活時的顏色。react-native
我整個導航配置都寫在一個獨立的JS文件裏,比較簡潔,能夠在其它地方引入和調用,看起來像這樣:app
//'use strict' import { TabNavigator } from 'react-navigation' // Tab-Navigators import TabOneNavigation from '../tabOne/views/TabOneNavigation' import TabTwoNavigation from '../tabTwo/views/TabTwoNavigation' import TabThreeNavigation from '../tabThree/views/TabThreeNavigation'
const routeConfiguration = {
TabOneNavigation: { screen: TabOneNavigation },
TabTwoNavigation: { screen: TabTwoNavigation },
TabThreeNavigation: { screen: TabThreeNavigation },
}
const tabBarConfiguration = { //...other configs tabBarOptions:{ // tint color is passed to text and icons (if enabled) on the tab bar activeTintColor: 'white', inactiveTintColor: 'blue', // background color is for the tab component activeBackgroundColor: 'blue', inactiveBackgroundColor: 'white', } }
export const TabBar = TabNavigator(routeConfiguration,tabBarConfiguration)
標籤欄構建好以後,尚未成型,不能進行實際渲染。咱們還要設置好每一個標籤導航器,使它們也能正確地渲染屏幕顯示。這些導航器咱們稱爲棧導航器(
StackNavigators
)。ide
根聽說明文檔,要構建一個棧導航器,須要調用一個和TabNavigator
很相似的函數叫StackNavigator(RouteConfigs, StackNavigatorConfig)
,一樣有兩個參數,只是這個函數的參數裏配置更多。參看說明文檔來獲取更多信息。函數
簡單地配置設定一下,和標籤欄配置差很少:flex
//'use strict' import { StackNavigator } from 'react-navigation' // Screens import TabOneScreenOne from './views/TabOneScreenOne' import TabOneScreenTwo from './views/TabOneScreenTwo' const routeConfiguration = { TabOneScreenOne: { screen: TabOneScreenOne }, TabOneScreenTwo: { screen: TabOneScreenTwo }, } // going to disable the header for now const stackNavigatorConfiguration = { headerMode: 'none', initialRouteName: 'TabOneScreenOne' } export const NavigatorTabOne = StackNavigator(routeConfiguration,stackNavigatorConfiguration) //只要改個名,就把三個標籤都設置好了。建立一個簡單的屏幕組件來渲染
import React from 'react' import { View, Text } from 'react-native' export default class TabOneScreenOne extends React.Component { render(){ return( <View style={{ flex:1, backgroundColor:'red', alignItems:'center', justifyContent:'center' }}> <Text>{ 'Tab One Screen One' }</Text> </View> ) } }
配置redux-store實例this
有一個頗有用的輔助函數叫getStateForAction
,它和路由掛鉤,並處理全部的導航邏輯。
這個函數在Redux store
實例中這樣用:
//'use strict' // Redux import { applyMiddleware, combineReducers, createStore } from 'redux' import logger from 'redux-logger' // Navigation import { NavigatorTabOne } from './tabOne/navigationConfiguration' import { NavigatorTabTwo } from './tabTwo/navigationConfiguration' import { NavigatorTabThree } from './tabThree/navigationConfiguration' import { TabBar } from './tabBar/navigationConfiguration' // Middleware const middleware = () => { return applyMiddleware(logger()) } export default createStore( combineReducers({ tabBar: (state,action) => TabBar.router.getStateForAction(action,state), tabOne: (state,action) => NavigatorTabOne.router.getStateForAction(action,state), tabTwo: (state,action) => NavigatorTabTwo.router.getStateForAction(action,state), tabThree: (state,action) => NavigatorTabThree.router.getStateForAction(action,state), }), middleware(), )
// React import React from 'react' import { AppRegistry } from 'react-native' // Redux import { Provider } from 'react-redux' import store from './app/store' // Navigation import TabBarNavigation from './app/tabBar/views/TabBarNavigation' class SampleNavigation extends React.Component { render(){ return( <Provider store={store}> <TabBarNavigation /> </Provider> ) } } AppRegistry.registerComponent('SampleNavigation', () => SampleNavigation)
還記得本文一開始,咱們建立了一些參數,而後傳入一個函數來建立標籤欄嗎?要將導航控制權從react-navigation
庫轉移到Redux State
實例中去,咱們須要給建立出來的標籤欄提供導航狀態,再用react-navigation
庫擁有的輔助函數來分派出去。爲標籤欄創建的文件像這樣:
// React import React from 'react' // Navigation import { addNavigationHelpers } from 'react-navigation' import { TabBar } from '../navigationConfiguration' //Redux import { connect } from 'react-redux' const mapStateToProps = (state) => { return { navigationState: state.tabBar, } } class TabBarNavigation extends React.Component { render(){ const { dispatch, navigationState } = this.props return ( <TabBar navigation={ addNavigationHelpers({ dispatch: dispatch, state: navigationState, }) } /> ) } } export default connect(mapStateToProps)(TabBarNavigation)
基本上和標籤欄同樣的方法。
import React from 'react' // Navigation import { addNavigationHelpers } from 'react-navigation' import { NavigatorTabOne } from '../navigationConfiguration' // Redux import { connect } from 'react-redux' // Icon import Icon from 'react-native-vector-icons/FontAwesome' const mapStateToProps = (state) => { return { navigationState: state.tabOne } } class TabOneNavigation extends React.Component { render(){ const { navigationState, dispatch } = this.props return ( <NavigatorTabOne navigation={ addNavigationHelpers({ dispatch: dispatch, state: navigationState }) } /> ) } } export default connect(mapStateToProps)(TabOneNavigation)
這樣應該能生成程序,運行程序而且在程序中導航了。但不怎麼好看
要改變標籤文字,加上圖標,只要把static navigationOptions
聲明放在各自的標籤導航器裏就好了。記得在標籤欄配置裏,咱們設置了tintColors
顏色,如今就能夠用這些顏色了。
第一個標籤導航器:
// React import React from 'react' // Navigation import { addNavigationHelpers } from 'react-navigation' import { NavigatorTabOne } from '../navigationConfiguration' // Redux import { connect } from 'react-redux' // Icon import Icon from 'react-native-vector-icons/FontAwesome' const mapStateToProps = (state) => { return { navigationState: state.tabOne } } class TabOneNavigation extends React.Component { static navigationOptions = { tabBarLabel: 'Tab One', tabBarIcon: ({ tintColor }) => <Icon size={ 20 } name={ 'cogs' } color={ tintColor }/> } render(){ const { navigationState, dispatch } = this.props return ( <NavigatorTabOne navigation={ addNavigationHelpers({ dispatch: dispatch, state: navigationState }) } /> ) } } export default connect(mapStateToProps)(TabOneNavigation)
看起來不錯。
如今咱們來處理標籤內部之間的導航。我要在每一個標籤的第一屏加一個按鈕,能導航到新的路線上去。
標籤一,屏幕一:
'use strict' import React from 'react' import { View, Text, TouchableOpacity } from 'react-native' export default class TabOneScreenOne extends React.Component { render(){ return( <View style={{ flex:1, backgroundColor:'red', alignItems:'center', justifyContent:'center' }}> <Text>{ 'Tab One Screen One' }</Text> <TouchableOpacity onPress={ () => this.props.navigation.navigate('TabOneScreenTwo') } style={{ padding:20, borderRadius:20, backgroundColor:'yellow', marginTop:20 }}> <Text>{'Go to next screen this tab'}</Text> </TouchableOpacity> </View> ) } }
標籤一,屏幕二:
use strict' import React from 'react' import { View, Text, TouchableOpacity } from 'react-native' export default class TabOneScreenTwo extends React.Component { render(){ return( <View style={{ flex:1, backgroundColor:'orange', alignItems:'center', justifyContent:'center' }}> <Text>{ 'Tab One Screen Two' }</Text> <TouchableOpacity onPress={ () => this.props.navigation.goBack() } style={{ padding:20, borderRadius:20, backgroundColor:'purple', marginTop:20 }}> <Text>{'Go back a screen'}</Text> </TouchableOpacity> </View> ) } }
如今全部的導航狀態都儲存在redux store實例中了。
有了這個信息,就能夠至關方便並任意地處理安卓系統回退鍵(AndroidBack
)行爲了。
若是想讓後退按鈕回到某一標籤的某一屏幕,只要加一個偵聽器便可。
BackHandler.addEventListener('hardwareBackPress', this.backAction ) backAction = () => { // get the tabBar state.index to see what tab is focused // get the individual tab's index to see if it's at 0 or if there is a screen to 'pop' if (you want to pop a route) { // get the navigation from the ref const { navigation } = this.navigator.props // pass the key of the focused route into the goBack action navigation.goBack(navigation.state.routes[navigation.state.index].key) return true } else { return false } } <TabWhateverNavigator ref={ (ref) => this.navigator = ref } navigation={ addNavigationHelpers({ dispatch: dispatch, state: navigationState }) } />
想不想經過屏幕上的按鈕跳轉至標籤呢?我想的。這裏有一個方法:
將getStateForAction
函數放到navigationConfiguration
文件裏去,這樣更好看些。讓它攔截自定義行爲或只是將同一個函數返回。
像這樣tabBar => navigationConfiguration,我這個例子目的就達到了
export const tabBarReducer = (state, action) => { if (action.type === 'JUMP_TO_TAB') { return { ...state, ...action.payload } } else { return TabBar.router.getStateForAction(action,state) } }
標籤三的屏幕上有一個按鈕
<TouchableOpacity onPress={ () => this.props.navigation.dispatch({ type:'JUMP_TO_TAB', payload:{index:0} }) } style={{ padding:20, borderRadius:20, backgroundColor:'deeppink', marginTop:20 }}> <Text>{'jump to tab one'}</Text> </TouchableOpacity>
還有新的store
實例
//...stuff and things import { TabBar, tabBarReducer } from './tabBar/navigationConfiguration' // more things export default createStore( combineReducers({ //...other stuff and more things tabBar: tabBarReducer, }), middleware(), )
原文:http://www.zcfy.cc/article/react-navigation-complete-redux-state-management-tab-bar-and-multiple-navigators-4449.html