React-Navigation redux集成


對於這個程序的頁面導航結構,我是這樣看的。首先,一個標簽欄本身就是自己的導航器,而上面每個標簽同樣是自己的導航器。在這個例子里,我要用帶有三個標簽的標簽欄,所以一共有四個導航器,每個導航器都有自己的還原器(reducer)和狀態。

我又把代碼分成幾個“功能”,所以整個結構看起來是這樣的:

/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)就是單個的標簽,也代表了單個的導航器。也就是說,這里每個單獨的標簽導航器都是我們給標簽欄設定的導航路線。

導航路線以鍵/值成對定義,比如ScreenName: { screen: ScreenName },所以路線配置參數就是一列可能要導航的路線。在這個例子里,導航路線是可能要轉到的標簽。

標簽導航設置(TabNavigatorConfig)參數是程序接口提供的選項,可以用來自定義標簽欄。這個參數只是由幾對鍵: 值結構組成的JS對象而已。其中最重要的一對是標簽欄選項(tabBarOptions),靠這個選項可以設定標簽激活時與不激活時的顏色。

我整個導航配置都寫在一個獨立的JS文件里,比較簡潔,可以在其它地方引入和調用,看起來像這樣:

//'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)。

棧導航器配置

根據說明文檔,要構建一個棧導航器,需要調用一個和TabNavigator很類似的函數叫StackNavigator(RouteConfigs, StackNavigatorConfig),同樣有兩個參數,只是這個函數的參數里配置更多。參看說明文檔來獲取更多信息。

簡單地配置設定一下,和標簽欄配置差不多:

//'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實例

有一個很有用的輔助函數叫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

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM