目前,我使用使用React Native / Redux的NavigationStateUtils来设置Push和Pop。 但是当触发Push动作的按钮不止一次被按下时,我得到这个错误: should not push * route with duplicated key *代表route.key或this.props.navKey 。
什么可能是错误的原因? 我应该如何使用NavigationStateUtils为每个单独的路线创建唯一的密钥?
这是我的设置 - Redux:
function mapStateToProps(state) { return { navigation: state.navReducer, } } export default connect( mapStateToProps, { pushRoute: (route) => push(route), popRoute: () => pop(), } )(NavigationRoot)我的reducer( 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这些方法处理推送和弹出以及导航栏返回(弹出)按钮的设置方式:
_renderScene (props) { const { route } = props.scene return ( <route.component _handleNavigate={this._handleNavigate.bind(this)} {...route.passProps} actions={this.props}/> ) } _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> ) } } } /> ) } } render() { return ( <NavigationCardStack direction={this.props.navigation.routes[this.props.navigation.index].direction} navigationState={this.props.navigation} onNavigate={this._handleNavigate.bind(this)} renderScene={this._renderScene} renderOverlay={this.renderOverlay} /> ) }并由组件调用,如下所示:
const route = { home: { type: 'push', route: { key: 'home', title: 'Home', component: Home, direction: 'vertical', } } }编辑控制台日志
编辑2继续
编辑3幻灯片菜单
Currently I have both Push and Pop set up using NavigationStateUtils using React Native/Redux. But when a button that triggers the Push action is pressed more than once, I get the error: should not push * route with duplicated key and * representing route.key or this.props.navKey.
What may be the cause of the error? How should I go about creating a unique key for each individual route using NavigationStateUtils?
This is my set up - Redux:
function mapStateToProps(state) { return { navigation: state.navReducer, } } export default connect( mapStateToProps, { pushRoute: (route) => push(route), popRoute: () => pop(), } )(NavigationRoot)My reducer (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 navigationStateAnd these methods handle push and pop and how navigation bar back (pop) button is set up:
_renderScene (props) { const { route } = props.scene return ( <route.component _handleNavigate={this._handleNavigate.bind(this)} {...route.passProps} actions={this.props}/> ) } _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> ) } } } /> ) } } render() { return ( <NavigationCardStack direction={this.props.navigation.routes[this.props.navigation.index].direction} navigationState={this.props.navigation} onNavigate={this._handleNavigate.bind(this)} renderScene={this._renderScene} renderOverlay={this.renderOverlay} /> ) }And called by components like so:
const route = { home: { type: 'push', route: { key: 'home', title: 'Home', component: Home, direction: 'vertical', } } }EDIT console log
EDIT 2 Continuation
EDIT 3 Slide Menu
最满意答案
像这样调试你的导航缩小器:
function navigationState (state = initialState, action) { switch(action.type) { case PUSH_ROUTE: console.log('action', action); if (state.routes[state.index].key === (action.route && action.route.key)) return state const newNavigationState = NavigationStateUtils.push(state, action.route); console.log('newNavigationState', newNavigationState); return newNavigationState; case POP_ROUTE: if (state.index === 0 || state.routes.length === 1) return state return NavigationStateUtils.pop(state) default: return state } }编辑
由于NavigationStateUtils.push工作方式(请参见此处 ),它需要一个全新的路由来推送到路由堆栈。 根据您的导航流程,您不能再次使用它来回家,您必须使用不同的功能,例如NavigationStateUtils.pop或NavigationStateUtils.reset以及其他功能(探索该文件)。 但是,您并不仅限于NavigationStateUtils的函数,您可以在Reducer中定义自己的修改,但需要确保导航状态具有以下数据格式:
{ // `routes` needs to be an array of objects and each object // needs to have a unique `key` property routes: [{ key: 'anything', // must be unique in this `routes` array ...anyOtherData }], // `index` is required, and has to be a number that refers // to the index of the route in the routes array that is active index: 1, }编辑
根据您在评论中的要求,每当您点击一个抽屉项目时,都需要重新设置以该路线为起点的导航路线堆栈,然后才能开始推送和弹出。
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) case DRAWER_ITEM_PRESS: // `action.route` is simply the route object of the drawer item you pressed return NavigationStateUtils.reset(state, [action.route], 0); default: return state; } }另一方面我注意到,根据你的要求,尝试使用react-native-router-flux可能是一个好主意,因为你可以定义你的路由 , 子路由 , 与react-native-drawer并与Redux集成 。
Debug your navigation reducer like this:
function navigationState (state = initialState, action) { switch(action.type) { case PUSH_ROUTE: console.log('action', action); if (state.routes[state.index].key === (action.route && action.route.key)) return state const newNavigationState = NavigationStateUtils.push(state, action.route); console.log('newNavigationState', newNavigationState); return newNavigationState; case POP_ROUTE: if (state.index === 0 || state.routes.length === 1) return state return NavigationStateUtils.pop(state) default: return state } }EDIT
Because of the way NavigationStateUtils.push works (see here), it requires a completely new route to push to the route stack. Based on your navigation flow, you cannot use it again to go back home, you have to use a different function like NavigationStateUtils.pop or NavigationStateUtils.reset and others as well (explore that file). However, you are not limited to just the functions in NavigationStateUtils, you can define your own modification in the reducer but you need to make sure the navigation state has this data format:
{ // `routes` needs to be an array of objects and each object // needs to have a unique `key` property routes: [{ key: 'anything', // must be unique in this `routes` array ...anyOtherData }], // `index` is required, and has to be a number that refers // to the index of the route in the routes array that is active index: 1, }EDIT
Based on your requirements in your comments, every time your hit a drawer item, it needs to reset your navigation route stack having that route as your starting point, then you can start pushing and popping.
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) case DRAWER_ITEM_PRESS: // `action.route` is simply the route object of the drawer item you pressed return NavigationStateUtils.reset(state, [action.route], 0); default: return state; } }As another side note, I see that based on your requirements, it would probably be a good idea to try using react-native-router-flux because you will be able to define your routes, sub-routes, integrate with react-native-drawer and integrate with Redux.
更多推荐
发布评论