2016-08-24 106 views
2

在React Native + Redux中,根據路線動態更改direction屬性(適用於過渡動畫),<NavigationCardStack/>的正確方法是什麼?如何在React Native中動態更改<NavigationCardStack />的方向?

例如,當我用<Navigator/>,我成立了configureScene財產動態如此,它根據不同的路線工作:

configureScene(route, routeStack) { 
    if(route.type === 'Modal'){ 
     return Navigator.SceneConfigs.FloatFromBottom 
    } 
    return Navigator.SceneConfigs.PushFromRight 
    } 

<Navigator 
    configureScene={this.configureScene} 
    ... 
/> 

編輯

這是我設置 - 終極版:

function mapStateToProps(state) { 
    return { 
    navigation: state.navReducer, 
    } 
} 

export default connect(
    mapStateToProps, 
    { 
    pushRoute: (route) => push(route), 
    popRoute:() => pop(), 
    } 
)(NavigationRoot) 

我的還原劑(navReducer.js):

const initialState = { 
    index: 0, 
    key: 'root', 
    routes: [{ 
    key: 'login', 
    title: 'Login', 
    component: Login, 
    direction: 'horizontal', 
    }] 
} 

function navigationState (state = initialState, action) { 
    switch(action.type) { 
    case PUSH_ROUTE: 
     if (state.routes[state.index].key === (action.route && action.route.key)) return state 
    return NavigationStateUtils.push(state, action.route) 

    case POP_ROUTE: 
     if (state.index === 0 || state.routes.length === 1) return state 
     return NavigationStateUtils.pop(state) 

    default: 
    return state 

    } 
} 

export default navigationState 

而且這些方法處理的push和pop以及如何導航欄後面(彈出)按鈕被設置:

_handleBackAction() { 
    if (this.props.navigation.index === 0) { 
     return false 
    } 
    this.props.popRoute() 
    return true 
    } 

    _handleNavigate(action) { 
    switch (action && action.type) { 
     case 'push': 
     this.props.pushRoute(action.route) 
     return true 
     case 'back': 
     case 'pop': 
     return this._handleBackAction() 
     default: 
     return false 
    } 
    } 

renderOverlay = (sceneProps) => { 
if(0 < sceneProps.scene.index) 
{ 
    return (
    <NavigationHeader 
     {...sceneProps} 
     renderLeftComponent={() => { 
     switch(sceneProps.scene.route.title){ 
      case 'Home': 
      return (
       <TouchableHighlight onPress={() => this._handleBackAction()}> 
       <Text}>X</Text> 
       </TouchableHighlight> 
      ) 

並號召通過組件像這樣:

const route = { 
    home: { 
    type: 'push', 
    route: { 
     key: 'home', 
     title: 'Home', 
     component: Home, 
     direction: 'vertical', 
    } 
    } 
} 
+0

,似乎找到和你說它的工作原理是什麼? – ajmajmajma

+0

@ajmajmajma這對'',並會爲''工作不同。例如,必須傳入不同的參數,我應該返回什麼?導致'Navigator.SceneConfigs.FloatFromBottom'等不起作用。 –

+0

@Jo Ko這不可能直接。如果你看看NavigatorCardStack _renderScene方法。它使用傳遞給NavigatorCardStack的道具來確定方向。唯一可行的方法是根據包裝組件中的活動場景更改方向道具。 https://github.com/facebook/react-native/blob/master/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js – while1

回答

1

ActionTypes.js

/* 
* action types 
*/ 
export const PUSH_ROUTE = 'PUSH_ROUTE' 
export const POP_ROUTE = 'POP_ROUTE' 

navigation.js(導航動作創建者)

import { PUSH_ROUTE, POP_ROUTE} from '../constants/ActionTypes' 
export function pushRoute(route){ 
    return ({ 
    type: PUSH_ROUTE, 
    route 
    }) 
} 
export function popRoute(){ 
    return ({ 
    type:POP_ROUTE 
    }) 
} 

navigation.js(導航減速器)

import { PUSH_ROUTE, POP_ROUTE } from '../constants/ActionTypes' 
import { NavigationExperimental } from 'react-native' 

const { 
    StateUtils: NavigationStateUtils 
} = NavigationExperimental 
//direction : horizontal vertical 
const initialState = { 
    index: 0, 
    routes: [{ 
    key: 'tabapp', 
    direction: 'horizontal' 
    }] 
} 

function navigation(state = initialState, action){ 
    switch(action.type) { 
    case PUSH_ROUTE: 
     if(state.routes[state.index].key === (action.route && action.route.key)) 
     return state 
     return NavigationStateUtils.push(state, action.route) 

    case POP_ROUTE: 
     if(state.index === 0 || state.routes.length === 1) 
     return state 
     return NavigationStateUtils.pop(state) 

    default: 
     return state 
    } 
} 
export default navigation 

D8Navigator.js(應用導航)

//Note:NavigationCardStack dirction 


import React, { Component } from 'react'; 
import { 
    NavigationExperimental, 
    StyleSheet, 
    Text, 
    View 
} from 'react-native'; 
import { connect } from 'react-redux' 
import { pushRoute, popRoute } from './actions/navigation' 
import D8TabsView from './tabs/D8TabsView' 
import LoginView from './tabs/user/LoginView' 

const { 
    CardStack: NavigationCardStack 
} = NavigationExperimental 

class D8Navigator extends Component { 
    constructor(props){ 
    super(props) 
    this._onPopRoute = this._onPopRoute.bind(this) 
    this._renderScene = this._renderScene.bind(this) 
    } 
    _onPopRoute(){ 
    this.props.dispatch(popRoute()) 
    } 
    _renderScene(sceneProps){ 
    const {route} = sceneProps.scene 
    switch(route.key){ 
     case "tabapp": 
     return (
     <D8TabsView /> 
    ) 
     case "loginview": 
     return (
      <LoginView /> 
     ) 
     default: 
     return 
    } 
    } 
    render(){ 
    let { navigationState } = this.props 
    return (
     <NavigationCardStack 
     direction={navigationState.routes[navigationState.index].direction} 
     navigationState={this.props.navigationState} 
     onNavigateBack={this._onPopRoute} 
     renderScene={this._renderScene} /> 
    ) 
    } 
} 

function mapStateToProps(state){ 
    return { 
    navigationState: state.navigation 
    } 
} 
export default connect(mapStateToProps)(D8Navigator) 

D8TabsView.ios.js

... 
import { connect } from 'react-redux' 
import { pushRoute, popRoute } from '../actions/navigation' 
... 
render(){ 
     return (
      <TabBarIOS 
     tintColor="#f33" 
     barTintColor="#fff" 
     unselectedTintColor="#888"> 
       ..... 

     <Icon.TabBarItemIOS 
      title="My" 
      iconName="ios-person-outline" 
      selectedIconName="ios-person" 
      selected={this.state.selectedTab === 'starred'} 
      onPress={() => { 
      this.props.dispatch(pushRoute({ 
       key:'loginview', direction:'vertical' 
      })) 
      }}> 
     </Icon.TabBarItemIOS> 
      </TabBarIOS> 
     ) 
    } 

「我的」tabBarItem onPress使用'垂直'按鈕調度loginview場景。

LoginView.js

import React, { Component } from 'react'; 
import { 
    StyleSheet, 
    Text, 
    View, 
    TouchableHighlight 
} from 'react-native'; 
import { connect } from 'react-redux' 
import { popRoute } from '../../actions/navigation' 

class LoginView extends Component { 
    render(){ 
    return (
     <View> 
     <Text>Login</Text> 
     <TouchableHighlight 
      underlayColor="#D0D0D0" 
      onPress={()=>this.props.dispatch(popRoute())}> 
      <Text> 
      Close 
      </Text> 
     </TouchableHighlight> 
     </View> 
    ) 
    } 
} 
export default connect()(LoginView) 
+1

非常感謝!它的作品非常漂亮。在我接受答案之前還有一個問題,對於popRoute(),我怎麼能用同樣的方向彈出?例如,如果'push'與方向'vertical'製成,當它被彈出,我該怎麼做,以便它在'vertical'方向做呢?謝謝! –

+1

如果與方向「垂直的」推製成,彈出時,「垂直」方向是自動進行。由於與方向「垂直」推時,NavigationCardStack的方向設定爲「垂直」,彈出時,在「垂直」方向確實也是如此。 – Nirvana

+0

這是非常奇怪的話,因爲即使它被推向'vertical',當'popped'就這樣做水平。難道我錯過了什麼?我用代碼更新了原始帖子。請看一下。 –

0

當你的導航卡是指路由對象和導航流行的動作發生, 您的導航卡失去了參考的彈出操作刪除路由對象。

所以我建議寫在底部的方法。

/constants/ActionTypes.js

// navigation action types 
export const NAV_PUSH_ROUTE = 'NAV_PUSH_ROUTE'; 
export const NAV_POP_ROUTE = 'NAV_POP_ROUTE'; 

// tab action types 
export const TAB_SELECT = 'TAB_SELECT'; 

/actions/navigationActions.js

/** 
* Navigation Actions 
* @flow 
*/ 

import { NAV_POP_ROUTE, NAV_PUSH_ROUTE } from '../constants/ActionTypes'; 

const navPush = route => { 
    const rtRoute = route; 
    if (route && route.direction === undefined) rtRoute.direction = 'horizontal'; 

    return { 
    type: NAV_PUSH_ROUTE, 
    route: rtRoute, 
    }; 
}; 

const navPop =() => (
    { 
    type: NAV_POP_ROUTE, 
    } 
); 

module.exports = { 
    navPush, 
    navPop, 
}; 

/減速器/ navigationReducer。JS

/** 
* Navigation reducer 
* @flow 
*/ 

import { NavigationExperimental } from 'react-native'; 

import { NAV_POP_ROUTE, NAV_PUSH_ROUTE } from '../constants/ActionTypes'; 

const { 
    StateUtils: NavigationStateUtils, 
} = NavigationExperimental; 

const initialState = { 
    index: 0, 
    key: 'global', 
    routes: [ 
    { 
     key: 'feed', 
     title: 'Feed', 
    }, 
    ], 
}; 

const navigationState = (state = initialState, action) => { 
    switch (action.type) { 
    case NAV_PUSH_ROUTE: 
     if (state.routes[state.index].key === (action.route && action.route.key)) { 
     return state; 
     } 

     const navigationNewState = NavigationStateUtils.push(state, action.route); 
     return { 
     ...navigationNewState, 
     direction: action.route.direction, 
     }; 

    case NAV_POP_ROUTE: 
     if (state.index === 0 || state.routes.length === 1) { 
     return state; 
     } 
     return NavigationStateUtils.pop(state); 

    default: 
     return state; 
    } 
}; 

module.exports = navigationState; 

/components/global/ECNavigator.js

/** 
* Everychoose navigation compoment for example 
* @flow 
*/ 

import React, { Component, PropTypes } from 'react'; 
import { 
    NavigationExperimental, 
    BackAndroid, 
    TouchableHighlight, 
    PixelRatio, 
    StyleSheet, 
    Text, 
    View, 
} from 'react-native'; 
import { connect } from 'react-redux'; 
import { navPush, navPop } from '../../../actions'; 

import LoginScreen from '../../views/LoginScreen'; 

const { 
    CardStack: NavigationCardStack, 
} = NavigationExperimental; 

const styles = StyleSheet.create({ 
    container: { 
    flex: 1, 
    justifyContent: 'center', 
    alignItems: 'center', 
    backgroundColor: '#fff', 
    }, 
    row: { 
    padding: 15, 
    backgroundColor: '#30b3d8', 
    borderBottomWidth: 1/PixelRatio.get(), 
    borderBottomColor: '#CDCDCD', 
    }, 
    rowText: { 
    fontSize: 17, 
    }, 
    buttonText: { 
    fontSize: 17, 
    fontWeight: '500', 
    }, 
}); 

const routes = { 
    loginScreenV: { 
    type: 'push', 
    route: { 
     key: 'LoginScreen', 
     title: 'Login screen vertical', 
     direction: 'vertical', 
    }, 
    }, 
    loginScreenH: { 
    type: 'push', 
    route: { 
     key: 'LoginScreen', 
     title: 'Login screen horizontal', 
    }, 
    }, 
}; 

class ECNavigator extends Component { 
    constructor(props) { 
    super(props); 

    this._renderScene = this._renderScene.bind(this); 
    this._handleBackAction = this._handleBackAction.bind(this); 
    this._handleNavigate = this._handleNavigate.bind(this); 
    } 

    componentDidMount() { 
    BackAndroid.addEventListener('hardwareBackPress', this._handleBackAction); 
    } 

    componentWillUnmount() { 
    BackAndroid.removeEventListener('hardwareBackPress', this._handleBackAction); 
    } 

    _handleBackAction() { 
    if (this.props.navigation.index === 0) { 
     return false; 
    } 

    this.props.popRoute(); 
    return true; 
    } 

    _handleNavigate(action) { 
    switch (action && action.type) { 
     case 'push': 
     this.props.pushRoute(action.route); 
     return true; 

     case 'back': 
     case 'pop': 
     return this._handleBackAction(); 

     default: 
     return false; 
    } 
    } 

    _renderScene(props) { 
    const { route } = props.scene; 

    switch (route.key) { 
     case 'LoginScreen': 
     return <LoginScreen navGoBack={this._handleBackAction} />; 
     default: 
     return (
      <View style={styles.container}> 
      <TouchableHighlight 
       style={styles.row} 
       underlayColor="#d0d0d0" 
       onPress={() => this._handleNavigate(routes.loginScreenV)} 
      > 
       <Text style={styles.buttonText}> 
       Go to LoginScreen (Vertical) 
       </Text> 
      </TouchableHighlight> 
      <TouchableHighlight 
       style={styles.row} 
       underlayColor="#d0d0d0" 
       onPress={() => this._handleNavigate(routes.loginScreenH)} 
      > 
       <Text style={styles.buttonText}> 
       Go to LoginScreen (Horizontal) 
       </Text> 
      </TouchableHighlight> 
      </View> 
     ); 
    } 
    } 

    render() { 
    const { navigation } = this.props; 

    return (
     <NavigationCardStack 
     style={{ flex: 1 }} 
     direction={navigation.direction} 
     onNavigateBack={this._handleBackAction} 
     navigationState={this.props.navigation} 
     onNavigate={this._handleNavigate} 
     renderScene={this._renderScene} 
     /> 
    ); 
    } 
} 

ECNavigator.propTypes = { 
    navigation: PropTypes.object, 
    pushRoute: PropTypes.func, 
    popRoute: PropTypes.func, 
}; 

const select = store => ({ 
    navigation: store.navigation, 
}); 

const actions = dispatch => ({ 
    pushRoute: route => dispatch(navPush(route)), 
    popRoute:() => dispatch(navPop()), 
}); 

module.exports = connect(select, actions)(ECNavigator); 

/components/view/LoginScreen.js

/** 
* LoginScreen compoment 
* @flow 
*/ 

import React, { PropTypes } from 'react'; 
import { 
    TouchableHighlight, 
    PixelRatio, 
    StyleSheet, 
    Text, 
    View, 
} from 'react-native'; 
import { connect } from 'react-redux'; 
import { navPush, navPop } from '../../../actions'; 

const styles = StyleSheet.create({ 
    container: { 
    flex: 1, 
    justifyContent: 'center', 
    alignItems: 'center', 
    backgroundColor: '#fff', 
    }, 
    row: { 
    padding: 15, 
    backgroundColor: '#30b3d8', 
    borderBottomWidth: 1/PixelRatio.get(), 
    borderBottomColor: '#CDCDCD', 
    }, 
    rowText: { 
    fontSize: 17, 
    }, 
    buttonText: { 
    fontSize: 17, 
    fontWeight: '500', 
    }, 
}); 

const LoginScreen = props => (
    <View style={styles.container}> 
    <Text style={styles.text}>Login Screen</Text> 
    <TouchableHighlight 
     style={styles.row} 
     underlayColor="#d0d0d0" 
     onPress={props.navGoBack} 
    > 
     <Text style={styles.buttonText}> 
     Go back 
     </Text> 
    </TouchableHighlight> 
    </View> 
); 

module.exports = LoginScreen; 

Result screen - GIF

相關問題