Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 navigationState

And 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

enter image description here enter image description here

EDIT 2 Continuation

enter image description here

EDIT 3 Slide Menu

enter image description here

like image 695
Jo Ko Avatar asked Sep 06 '16 03:09

Jo Ko


People also ask

What is navigation in react router and Redux?

Put simply, navigation refers to the ability to  move from one page to another. In this React Router and Redux tutorial, we’ll show you the nuances of navigating within your React/Redux applications and demonstrate how to do so declaratively.

What is the use of Redux in react?

Redux is a predictable state container designed to help you write JavaScript apps that behave consistently across client, server, and native environments and are easy to test. While it’s mostly used as a state management tool with React, you can use it with any other JavaScript framework or library.

How to push multiple instances of a route in stackrouter?

if a route with the given name already exists in the state, StackRouter will jump to the existing route, along with setting the new parameters. If, however, you want to push several instances of the same route, you can do so by providing a unique key parameter each time you call navigate, or you can use the push action available on StackRouter.

What is a react router?

What is React Router? React Router is a popular declarative way of managing routes in React applications. It takes away all of the stress that comes with manually setting routes for all of the pages and screens in your React application. React Router exports three major components that help us make routing possible — Route, Link, and BrowserRouter.


1 Answers

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.

like image 188
rclai Avatar answered Oct 20 '22 22:10

rclai