How to create a unique key for NavigationStateUtils using the push route in React Native / Redux?

I currently have Push and Pop using NavigationStateUtils using React Native / Redux. But when the button that triggers the Push action is pressed more than once, I get an error: should not push * route with duplicated key and * representing route.key or this.props.navKey .

What could be causing the error? How do I create a unique key for each individual route using NavigationStateUtils ?

This is my setup - 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 navigationState 

And these methods handle push and pop and how the navigation button (pop) is configured:

  _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 these components:

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

EDIT Console Log

enter image description here enter image description here

EDIT 2 Continued

enter image description here

EDIT 3 Slide Menu

enter image description here

+8
javascript ios reactjs redux react-native
source share
1 answer

Debug your navigation gear as follows:

 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

Due to how NavigationStateUtils.push works (see here ), this requires a completely new route to go to the route stack. Based on your navigation stream, you cannot use it again to return home, you need to use another function, for example NavigationStateUtils.pop or NavigationStateUtils.reset and others (examine this file). However, you are not limited only to functions in NavigationStateUtils , you can define your own modification in the reducer, but you need to make sure that 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

According to your requirements in your comments, every time you click on a box item, it needs to reset your navigation route stack having that route as a starting point, then you can start pushing and popping up.

 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 note, I see that, based on your requirements, it would be nice to try using react-native-router-flux because you can define routes , sub react-native-drawer , integrate with react-native-drawer and integrate with Redux .

+4
source share

All Articles