React-Navigation redux集成

對於這個程序的頁面導航結構,我是這樣看的。首先,一個標籤欄自己就是本身的導航器,而上面每一個標籤一樣是本身的導航器。在這個例子裏,我要用帶有三個標籤的標籤欄,因此一共有四個導航器,每一個導航器都有本身的還原器(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)

這樣應該能生成程序,運行程序而且在程序中導航了。但不怎麼好看

讓咱們去掉那些難看的文字,加些iOS系統圖標吧。

要改變標籤文字,加上圖標,只要把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

相關文章
相關標籤/搜索