在React Native + Redux中,根据路由,为direction
动态更改过渡动画的<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
这些方法处理推送和弹出以及如何设置导航栏后退(弹出)按钮:
_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 :(得分: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使用'vertical'调度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 :(得分: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,
};
/reducer/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;