Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reset to nested route, "There is no route defined for..." #1127

Closed
aroth opened this issue Apr 20, 2017 · 74 comments
Closed

Reset to nested route, "There is no route defined for..." #1127

aroth opened this issue Apr 20, 2017 · 74 comments

Comments

@aroth
Copy link

aroth commented Apr 20, 2017

PR #1025 has resolved issues when attempting to navigate between nested navigators (#983, #704). Thanks for the fix, @grabbou.

Would it be possible for reset to behave the same way? Consider this navigation structure:

  • AppNavigator (StackNav)
    • SplashScreen
    • Unauthorized (StackNav)
      • WelcomeScreen
      • LoginScreen
      • SignUpScreen
    • Authorized (TabNav)
      • Tab1 (StackNav)
        • Screen1
        • Screen2
        • ScreenN
      • Tab2 (StackNav
        • Screen...
const UnauthorizedNavigator = StackNavigator({
  Welcome: { screen: WelcomeScreen },
  Login: { screen: LoginScreen },
  SignUp: { screen: SignUpScreen },
}, {
  initialRouteName: 'Welcome',
  headerMode: 'screen',
});

const AuthorizedNavigator = TabNavigator({
  MyProfile: {
    screen: MyProfileNavigator,
    path: '/',
    navigationOptions: ({ navigation }) => ({
      tabBarLabel: 'My Profile',
    }),
  },
  // ... + more tabs w/ stack navigators 
});

const AppNavigator = StackNavigator({
  Splash: { screen: SplashScreen },
  Unauthorized: { screen: UnauthorizedNavigator },
  Authorized: { screen: AuthorizedNavigator },
}, {
  initialRouteName: 'Splash',
  headerMode: 'none',
});

On Sign Up or Login, I'd like to reset to Authorized.
On Sign Out, I'd like to reset to Unauthorized.

In master, if I attempt to reset I'm getting There is no route defined for key Authorized. Must be one of: Welcome, Login, SignUp.
If I navigate, it works.

Strangely enough, reset does work properly if I wrap the components like so:

function wrap(WrappedComponent) {
  const EnhancedComponent = class extends React.Component {
    render() {
      return <WrappedComponent />
    }
  }
  return EnhancedComponent;
}

const AppNavigator = StackNavigator({
  Splash: { screen: SplashScreen },
  Unauthorized: { screen: wrap(UnauthorizedNavigator) },
  Authorized: { screen: wrap(AuthorizedNavigator) },
}, {
  initialRouteName: 'Splash',
  headerMode: 'none',
});

resetTo(route) {
  const actionToDispatch = NavigationActions.reset({
    index: 0,
    actions: [NavigationActions.navigate({ routeName: route })],
  });
  this.navigator.dispatch(actionToDispatch);
}

cc: @henrikra

@henrikra
Copy link

Yeah this needs to be fixed

@hasty
Copy link

hasty commented Apr 20, 2017

A quick workaround (not heavily tested): set the key property to null on your action.

resetTo(route) {
  const actionToDispatch = NavigationActions.reset({
    index: 0,
    key: null,
    actions: [NavigationActions.navigate({ routeName: route })],
  });
  this.navigator.dispatch(actionToDispatch);
}

@aroth
Copy link
Author

aroth commented Apr 20, 2017

@hasty Thanks, that works. I can remove the wrap(...) hack for now. Still interested in other thoughts.

@seclace
Copy link

seclace commented Apr 21, 2017

@hasty Thank you) This works for me. But the "index" property is not need to add for this. (May be in my case only)

@grabbou
Copy link

grabbou commented Apr 22, 2017

cc: @ericvicenti

@antsmo
Copy link

antsmo commented Apr 24, 2017

I just ran in to this issue too, would be nice to see more fine grained targeting for actions when using nested navigators.

@ofairfoul
Copy link

thanks @hasty setting key to null worked for me. I had the same issue with the Back action with nested navs.

@kelset
Copy link

kelset commented Apr 25, 2017

Unluckily setting the key:null didn't work for me, I'm really confused at the moment: here's a strange behaviour I can't really understand.

Basically, my nesting goes like this:

StackNavigator (where I have RootStackLogin, etc.)
|--> TabNavigator
     |--> StackNavigator (where I have my screen with the reset function)
|--> (another TabNavigator)
     |--> StackNavigator (where I have a TestScreen)

Given that I pass as route the string 'RootStackLogin', the reset method proposed by @hasty returns me this error: There is no route defined for key RootStackLogin. Must be one of: 'TestScreen'

but, if I write it this way:

    const actionToDispatch = NavigationActions.reset({
      index: 0,
      key: null,
      actions: [NavigationActions.navigate({ route })],  // not passing the proper parameter
    });
    this.props.navigation.dispatch(actionToDispatch);

It returns There is no route defined for key undefined. Must be one of: 'RootStackModal','RootStackLoading','RootStackLogin','RootStackLogged' - so in this case it checks for the route existence in the StackNavigator I want it to reset to. Why isn't it giving me the same warning as above, telling me that it needs to be TestScreen?

@hasty
Copy link

hasty commented Apr 25, 2017

Two things:

  1. You're passing an object with the key "route" to navigate, but it expects "routeName".
  2. I think in this nested case you may need to pass a second action, e.g.
 const actionToDispatch = NavigationActions.reset({
      index: 0,
      key: null,
      actions: [NavigationActions.navigate({ routeName:"<the name of the parent navigator for TestScreen>" }), NavigationActions.navigate({ routeName:"TestScreen" })],  // not passing the proper parameter
    });
    this.props.navigation.dispatch(actionToDispatch);

@kelset
Copy link

kelset commented Apr 26, 2017

They @hasty, thanks for your feedback:

  1. Yes, I know (that's why there's the // not passing the proper parameter comment inline ;) ) - I just wanted to point of how that method behaves differently. It checks two different navigators, apparently, and that confuses me.

  2. I tried your approach, unluckily, I keep having the same error as above.

@betocantu93
Copy link

betocantu93 commented Apr 26, 2017

Tried @hasty approach, unfortunately, without success.
What I also tried in one of my many attempts to get this working was to first dispatch a nested navigation and then execute a reset, but I'm getting some errors also.

let routeName='Login';
  let subRoute='Credentials';
  const nestedNavigation = NavigationActions.navigate({
    routeName: routeName,
    action: NavigationActions.navigate({routeName: subRoute})
  });
  navigation.dispatch(nestedNavigation);

  const reset = NavigationActions.reset({
    index: 0,
    actions: [NavigationActions.navigate({ routeName: routeName })]
  })

navigation.dispatch(reset);

Which gives me the following error: There is no route defined for key Login. Must be one of: Credentials, Access (and I'm at MainNavigator, so this makes no sense to me)

So I literally changed Login to Credentials as literally stated on the Error, and got this new Warning: setState(...) can only update a mounted or mounting....

Which is just a warning BUT, stills my navigation is not reset, I still can go back.

Of course I also tried to stack up all actions nested NavigationActions as stated in docs

action - Object - Optional - (advanced) The sub-action to run in the child router, if the screen is a navigator. Any one of the actions described in this doc can be set as a sub-action. https://reactnavigation.org/docs/navigators/navigation-actions#reset

@jamesisaac
Copy link

@betocantu93 What worked for me is almost exactly what you have, except I wrapped the second dispatch in a setImmediate(). You can debug up to this point by starting with using something like setTimeout(() => { navigation.dispatch(reset) }, 1000), which should work (as by that point you definitely are on the correct navigator), and then seeing that it works all the way down to zero.

I don't know enough about the internals to really explain why that worked, but I guess the first navigate action is asynchronous.

@matthamil
Copy link
Member

I think this is a feature that is requested: #1206

@betocantu93
Copy link

@jamesisaac Thanks alot for your answer, I'm working towards implementing/debugging your approach, but when reset action proceeds (after timeout) it seems to completely ignore the whole purpose of reset since the stack stills shows on the middleware the navigation history stack on New State; nothing happens, Login screen stills render the back button (And in other scenario I maybe just would hide it or something) but here it's not possible because of android's back button.

@betocantu93
Copy link

betocantu93 commented Apr 26, 2017

Ok what I did, and I don't like it.

I have this:

Splash: {screen: Splash},
Login: {screen: LoginStack},
Home: {screen: HomeStack}

  • HomeStack
    --TabNav
    ---AddVehicle

I navigated from a HomeStack screen (AddVehicle) to Splash, the only thing Splash do is check credentials and a normal parent reset (single navigation action). Somehow, it worked.

BUT right now I can see the back navigation animation going through all of the previous screens, which I don't like.

@matthamil
Copy link
Member

matthamil commented Apr 26, 2017

Yeah, the setTimeout workaround forces your user to watch the app navigate (since you are waiting on each transition animation to complete before calling the next navigate()). Hopefully #1206 (if it gets implemented) will allow you to transition to another nested screen without having to do this setTimeout hack.

@ghost
Copy link

ghost commented Apr 28, 2017

Similar situation here. My ideal navigation structure would be Stack Navigator wrapping single Stack Navigator (Login) and a Drawer Navigator with multiple child Stack Navigators. Right now, everything is wrapped in a Drawer Navigator so I can navigate as best as I can without using reset, the downside is that a user can open the drawer with a swipe action on the Login screen.

Even with my current implementation, reset won't work as described in this issue. Here is where I'm trying to trigger it from.

  • DrawerNavigator
    • Login
    • Home
    • Settings
      • index
      • Notifications
      • Profile (Logout button, reset to Login screen)
There is no route defined for key Login. Must be one of: 'index', 'Notifications', 'Profile'

My dispatch action

dispatch(NavigationActions.reset({
  index: 0,
  actions: [
    NavigationActions.navigate({
      routeName: 'Login',
    })
  ]
}));

@matthamil
Copy link
Member

@RobertSheaO I have a similar set up, except I pull the LogIn screen one navigator back:

  • Stack Navigator
    • LogIn
    • Register
    • DrawerNavigator
      • TabNavigator
        • Home
        • Feed
        • Friends

With this setup, I can't swipe to open the drawer from the Login screen.

@ghost
Copy link

ghost commented Apr 28, 2017

Yes @matthamil, that is my ideal setup, but then I run into even more issues with reset not working. Do you have an example of reset working inside your app, that takes them out of your DrawerNavigator and back to the Login screen?

@matthamil
Copy link
Member

matthamil commented Apr 28, 2017

Sure:

import { NavigationActions } from 'react-navigation';

// inside my class component
logoutUser() {
    const { logoutUser } = this.props;
    logoutUser(); // clears cache of user info
    navigation.navigate('LogIn');
    const resetAction = NavigationActions.reset({
      index: 0,
      actions: [
        NavigationActions.navigate({ routeName: 'LogIn' })
      ]
    });
    setTimeout(this.props.navigation.dispatch.bind(null, resetAction), 500);
}

This is a bit of a hack given that you can't dispatch multiple navigation actions at once. What I'm doing is navigating to the log in screen, then half a second later resetting the navigation stack. This prevents the user from pressing the Android "back" button to return to the previous screen.

@ghitatomy
Copy link

Don't reset and will work! something like NavigationActions.navigate({ routeName: 'LogIn' })

@matthamil
Copy link
Member

matthamil commented May 3, 2017

@ghitatomy In this case, reset is needed. If the user logs out on their Android phone, they will still be able to hit the Back button and navigate to the previous screen which could crash the app or give them access to things they are no longer authorized to use. navigate will not reset the stack.

Also, depending on your nested navigation structure, doing navigate will prompt you with a warning that "There is no route for X".

@ghitatomy
Copy link

You can reset it on logout.

@EdoFoco
Copy link

EdoFoco commented May 19, 2017

Hi all,

Did anyone get this working?

@cbjs
Copy link

cbjs commented May 22, 2017

1.0.0-beta.10 still not working

@evilantnie
Copy link

This issue has forced me to add a bunch of navigation outside of react-navigation, to the point that I question why I'm still using it. I love where this project is heading it just seems to be missing a nice way of managing global navigation from within nested states.

Anyone find easier ways? none of the solutions in this thread, or elsewhere seem to work for me.

@evilantnie
Copy link

Since reset doesn't seem to understand how to traverse state relationships between nested navigators, the current hack i've settled on is to define reducers for each navigator that handle the state transitions independently.

Reset still doesn't work in that context, but you can NavigationActions.navigate to the correct screen at each level. This leads to a bit of duplication, which a proper top-level reset method would fix, but it does solve my navigation problems.

@cj4luv
Copy link

cj4luv commented Nov 3, 2017

const resetAction = (routeName) => NavigationActions.reset({ index: 0, actions: [ NavigationActions.navigate({ routeName: routeName, key: null }) ], key: null });

insert your TabNavigator

navigationOptions:({ navigation }) => ({ tabBarOnPress: (scene, jumpToIndex) => { // console.log('onPress:', scene); jumpToIndex(scene.index); setTimeout(() => {navigation.dispatch(resetAction(scene.route.routes[0].routeName))}, 10); }, }),

Hope it will help you.

@namxam
Copy link

namxam commented Nov 15, 2017

I have almost the same issue as a few other people with a nav tree like

Root
-> Guest
--> SignIn
--> SignUp

-> Authorized (Tabs)
--> Home (Tabs)
---> Page1
---> Page2
--> Settings (Stack)
---> Page3
---> Page4

In order to jump from Page4 to Page1 when clicking on a nav, I now use:

Settings: {
      screen: SettingsNavigator,
      navigationOptions: ({ navigation }) => {
        return {
          tabBarLabel: 'Settings',
          tabBarOnPress: async (tab, jumpToIndex) => {
            await jumpToIndex(tab.index);
            await navigation.dispatch(
              NavigationActions.reset({
                index: 0,
                key: 'SettingsNavigator',
                actions: [NavigationActions.navigate({ routeName: 'SettingsList' })],
              })
            );
          },
        };
      },
    },

I guess it is almost the same solution as the suggested setTimeout, except that you don't need it and just wait for navigation to finish by using async/await.

Is this feasible or am I just lucky that it is working?

@cyphire
Copy link

cyphire commented Nov 26, 2017

I am replacing my former comments with the following.

First of all thank you all who are working to make this better, unpaid and often without credit from others. As I get better at this I will attempt to also help out.

Thank you for the following comments which really helped. I will look them over for guidance.

I went back to all my notes and found the following:

// https://github.com/react-community/react-navigation/issues/1254
const noTransitionConfig = () => ({
  transitionSpec: {
    duration: 0,
    timing: Animated.timing,
    easing: Easing.step0
  }
})

Inserting the call to that into my DrawerNavigator:

// https://github.com/react-community/react-navigation/issues/1254
const noTransitionConfig = () => ({
  transitionSpec: {
    duration: 0,
    timing: Animated.timing,
    easing: Easing.step0
  }
})

Allowed me to make a call to my Drawer and thus let me reset my route...

I made a pretty large app which works, but without the best practices and knowledge of where exactly everything goes, it was unwieldy. I am back in the game to make my app work and work correctly, I am using ignite as a nice starting place to get my redux, reactotron, saga, and other parts in line. My previous comments on react-native-router-flux was just that it was working, and for the last year I was worried about the fact that react-native has told the community that react-navigation is the future.

Anyway thanks for all your help and advice.

@ghost
Copy link

ghost commented Nov 26, 2017

@cyphire Sorry React Navigation isn't working for you. To clarify, React Navigation came out in late January of this year (2017) and this issue was opened in April. It's also fair to point out that React Navigation is still in Beta. With that being said, I 100% feel your frustration. I've been using React Navigation since it came out and it took while to get it right. A lot of issues I've had required me to go back to the drawing board and really think about what and why I was trying to approach it in the way I was. Here's a few snippets of my app, which is in production on the App Store and Google Play. The only "hacky" thing is specifying key: null in my reset.

Setup Stuff

App.js (imported by index.ios.js/index.android.js)
export default class App extends Component {
  render () {
    return (
      <Provider store={store}>
        <RootContainer />
      </Provider>
    )
  }
}
RootContainer.js
class RootContainer extends Component {
  render () {
    return (
      <View style={styles.applicationView}>
        <ReduxNavigation />
      </View>
    )
  }
}
export default connect(null, mapDispatchToProps)(RootContainer)
ReduxNavigation.js
function ReduxNavigation (props) {
  const { dispatch, nav } = props
  const navigation = ReactNavigation.addNavigationHelpers({
    dispatch,
    state: nav
  })

  return <AppNavigation navigation={navigation} />
}

const mapStateToProps = state => ({ nav: state.nav })
export default connect(mapStateToProps)(ReduxNavigation)

React Navigation Setup

AppNavigation.js
const PrimaryNav = StackNavigator({
  LoginScreen: { screen: LoginScreen },
  AppScreen: { screen: TabNavigation }
}, {
  headerMode: 'none',
  initialRouteName: 'LoginScreen',
  navigationOptions: {
    headerStyle: styles.header
  }
})
export default PrimaryNav
LoginScreen.js
class LoginScreen extends Component {
  render () {
    return (
      <Button onPress={() => this.props.onLogin()}>
        <Text>Login</Text>
      </Button>
  }
}
const mapStateToProps = (state) => {}

const mapDispatchToProps = (dispatch) => {
  return {
    onLogin: () => {
      const resetAction = NavigationActions.reset({
        index: 0,
        actions: [
          NavigationActions.navigate({routeName: 'AppScreen'})
        ],
        key: null
      })
      dispatch(resetAction)
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen)
TabNavigation.js
export default TabNavigator({
  Home: {
    screen: HomeNavigation,
    navigationOptions: {
      tabBarLabel: 'Home',
      tabBarIcon: ({ tintColor }) => <Icon name='home' color={tintColor} size={24} />
    }
  },
  Setting: {
    screen: SettingNavigation,
    navigationOptions: {
      tabBarLabel: 'Settings',
      tabBarIcon: ({ tintColor }) => <Icon name='settings' color={tintColor} size={24} />
    }
  }
}, {
  initialRouteName: 'Home',
  order: ['Home', 'Setting'],
  tabBarComponent: TabBarBottom,
  tabBarPosition: 'bottom',
  swipeEnabled: false,
  animationEnabled: false,
  lazy: true
})
SettingNavigation.js
const routes = {
  Index: { screen: SettingScreen },
  Support: { screen: SupportScreen },
  WebContent: { screen: WebContentScreen }
}

const config = {
  headerMode: 'float'
}

export default StackNavigator(routes, config)
SettingScreen.js
class LoginScreen extends Component {
  render () {
    return (
      <Button onPress={() => this.props.onLogout()}>
        <Text>Logout</Text>
      </Button>
  }
}
const mapStateToProps = (state) => {}

const mapDispatchToProps = (dispatch) => {
  return {
    onLogout: () => {
      const resetAction = NavigationActions.reset({
        index: 0,
        actions: [
          NavigationActions.navigate({routeName: 'LoginScreen'})
        ],
        key: null
      })
      dispatch(resetAction)
    }
  }
}
export default connect(mapStateToProps, mapDispatchToProps)(LoginScreen)

@ghost
Copy link

ghost commented Nov 26, 2017

BTW, If you want an app that has both React Navigation, Redux, and Sagas baked in, check out Ignite. Their setup takes away a lot of headaches of setting up an app from scratch. Personally, I trigger all my reset actions from Sagas, and my logout action is actually just a reset on my reducer. Here's a snippet of how I have my logout configured

import { combineReducers } from 'redux'
import { resettableReducer } from 'reduxsauce'

const resettable = resettableReducer('GLOBAL/LOGOUT')
const rootReducer = combineReducers({
  nav: resettable(require('./NavigationRedux').reducer),
  global: resettable(require('./GlobalRedux').reducer),
  login: resettable(require('./LoginRedux').reducer),
  home: resettable(require('./HomeRedux').reducer),
  setting: resettable(require('./SettingRedux').reducer),
  persistent: require('./PersistentRedux').reducer
})

P.S. React Native can take awhile to get the hang of, but once you do, it's well worth it. I've been using it a little over a year now and am very happy I did. I now have three full fledged apps written with it, and they all use React Navigation. Two of these apps are published at the time of this writing.

@kelset
Copy link

kelset commented Nov 26, 2017

Thanks @RobertSheaO for your comments, made my day.

@jnmandal
Copy link

I think we should try and migrate the discussion about the lib itself into the other thread @kelset mentioned (#2031). I appreciate both sides but at the end of the day this issue is for the bug affecting people. I imagine those ~40 devs watching this issue are mostly looking for notifications about when it has been fixed.

If @kelset, @RobertSheaO, or other folks more familiar with library could triage and point to where in code/dependencies/timeline this can be fixed, maybe one of us can step in to contribute towards a fix. My superficial understanding was we need to add something like in #1025 but probably some edgecase or section of codebase was missed in that PR.

React native itself is under very active development so bugs are bound to arise in ecosytem. Discussion that might help someone or myself to patch the issue would be appreciated. I mean this sincerely -- not trying to be snarky. This library and its contributors are doing great work trying to give us a decent, reliable navigation library. 🙏🏻

@cyphire
Copy link

cyphire commented Nov 26, 2017

Thanks @RobertSheaO and thank you @kelset !

I have a huge unwieldy application because there is no real guide to putting a big (whole) application together. I made the mistake of both trying to learn and use redux at the SAME TIME I changed the navigation from react-native-router-flux (Thanks @aksonov !) which worked, but I was afraid to stay with it because of the articles touting react-navigation.org as the future of react-native navigation.

I finally understand Redux enough to implement it! The ignite (again thank you!) way works great, the Reactotron configs actually work and can let me see my state, and I'm on my way to build my app the right way.

@kelset
Copy link

kelset commented Nov 26, 2017

Thanks @cyphire, I've removed my answer since it's no use anymore. As soon as @LAITONEN removes his I'll remove the other one and this issue will be back on topic as @jnmandal also hoped.
(all comments have been removed)

@rochapablo
Copy link

rochapablo commented Nov 30, 2017

@RobertSheaO,

This will work for the parent route

const resetAction = NavigationActions.reset({
      index: 0,
      actions: [
        NavigationActions.navigate({ routeName: 'Dashboard' })
      ]
    });
    this.props.navigation.dispatch(resetAction);

But if I try navigate to any child route from a diferente parent, it will crash

Error: There is no route defined for key Dashboard.
Must be one of: 'Login','Register'

This error is located at:
    in CardStack (at CardStackTransitioner.js:97)
    in RCTView (at View.js:113)
    in View (at Transitioner.js:187)
    in Transitioner (at CardStackTransitioner.js:49)
    in CardStackTransitioner (at StackNavigator.js:60)
    in Unknown (at createNavigator.js:52)
    in Navigator (at createNavigationContainer.js:216)
    in NavigationContainer (at navigator.js:78)
    in AppWithNavigationState (created by Connect(AppWithNavigationState))
    in Connect(AppWithNavigationState) (at App.js:17)
    in Provider (at App.js:16)
    in Backpack (at renderApplication.js:35)
    in RCTView (at View.js:113)
    in View (at AppContainer.js:102)
    in RCTView (at View.js:113)
    in View (at AppContainer.js:122)
    in AppContainer (at renderApplication.js:34)

Like here

const stack = StackNavigator(
  {
    Login: { screen: LoginComponent },
    Register: { screen: RegisterComponent }
  },
  {
    ...settings,
    initialRouteName: 'Login'
  }
)

const drawer = DrawerNavigator(
  {
    Dashboard: { screen: DashboardComponent },
    Help: { screen: HelpComponent }
  },
  {
    ...settings,
    drawerPosition: Platform.OS === 'ios' ? 'right' : 'left',
    contentComponent: (props) => <SidenavComponent {...props} />
  }
)

export const AppNavigator = StackNavigator(
  {
    LoginNav: { screen: stack },
    DrawerNav: { screen: drawer }
  },
  {
    ...settings,
    initialRouteName: 'LoginNav'
  }
)

If I'm on /LoginNav and call /Register will work

If I'm on /LoginNav and call /Dashboard will fail

@ghost
Copy link

ghost commented Nov 30, 2017

@rochapablo Try adding key: null to your reset, like so:

const resetAction = NavigationActions.reset({
  index: 0,
  actions: [
    NavigationActions.navigate({routeName: 'Dashboard'})
  ],
  key: null
})
this.props.navigation.dispatch(resetAction)

Here's a breakdown of my navigation stack:

MainNavigation (StackNavigator)
  LoginScreen
  OnboardingNavigation (StackNavigator)
    Screen1
    Screen2 - Reset is here (Reset to AppNavigation)
  AppNavigation (TabNavigator)
    Home (StackNavigation)
      Index
      Detail
    Setting (StackNavigation)
      Index - Logout button here attached to reducer via reduxsauce (dispatch {type: 'LOGOUT'))
      Support
      Profile

Here is a snippet of my reducer setup, where I listen for LOGOUT and reset nav, which is where React Navigation has the current state of the navigation (assuming you use redux):

import { combineReducers } from 'redux'
import { resettableReducer } from 'reduxsauce'

const resettable = resettableReducer('LOGOUT')
const rootReducer = combineReducers({
  nav: resettable(require('./NavigationRedux').reducer),
  login: resettable(require('./LoginRedux').reducer),
  global: resettable(require('./GlobalRedux').reducer),
  persistent: require('./PersistentRedux').reducer
})

@ScreamZ
Copy link

ScreamZ commented Dec 8, 2017

Any news on this ?

i have

TabNav

  • Home
  • Explore (Stack)
    -- A
    -- B
  • Profile

On profile i try to dispatch on logout click

 this.props.navigation.dispatch(
        NavigationActions.reset({
          index: 0,
          key: null,
          actions: [NavigationActions.navigate({ routeName: 'Home' })]
        })
      ) // Force to be on the Home screen

But this is matching the router of Explore... I don't know why

@reactnative103
Copy link

reactnative103 commented Jan 5, 2018

Same issue like "PR" @aroth and don't have any solution in above discussion, if anyone get the solution so please let us know. Its a very critical problem with react native. How we can move ahead with this technology. Its not good for React-Native Community. I am totally disappointed :( ......... 👎

@brunocascio
Copy link

Any update please?

@kkusanagi
Copy link

I test out new version recently and tried all old version from 1.0.1 until 1.1.2.
All dispatch can't use.

please use react-navigation@1.0.0-beta.19 for dispatch feature

@Ariflo
Copy link

Ariflo commented Feb 26, 2018

If you are like me and you landed on this thread because reseting the stack caused problems with the order of the stack, here's a fix:

So for example instead of building the stack inside of the actions prop:

        const reset = NavigationActions.reset({
          index: 1,
          actions: [
            NavigationActions.navigate({
              routeName: 'HOME_SCREEN',
            }),
            NavigationActions.navigate({
              routeName: 'PROFILE_SCREEN',
            }),
          ]
        })

this.props.navigation.dispatch(reset)

You need to instead reset the stack with one navigation action and dispatch any subsequent navigation actions afterwards:

        const reset = NavigationActions.reset({
          index: 0,
          actions: [
            NavigationActions.navigate({
              routeName: 'HOME_SCREEN',
            }),
          ]
        })

       const profile_page =  NavigationActions.navigate({
          routeName: PROFILE_SCREEN,
        })

this.props.navigation.dispatch(reset)
this.props.navigation.dispatch(profile_page)

For some reason the second .navigate() does not work as expected when combined. The index of the top route in the array (in this case HOME_SCREEN) is zero'd out and any action passed to either .navigate() are not executed.

The action below, for example, would not fire off inside of reset 😞

          NavigationActions.navigate({
            routeName: HOME_SCREEN,
            action: NavigationActions.navigate({ routeName: PROFILE_SCREEN }),
          }),

@kkusanagi
Copy link

kkusanagi commented Feb 28, 2018

@Ariflo thanks. seems like it really need to separate to different dispatch.

still, this is quite annoying. Cant we do like this?

const reset = NavigationActions.reset({
          index: 0,
          actions: [
            NavigationActions.navigate({
              key: null,
              routeName: 'Login_Screen',
            }),
            NavigationActions.navigate({
              key: 'Main',
              routeName: 'HOME_SCREEN',
            }),
            NavigationActions.navigate({
              key: 'Main',
              routeName: 'PROFILE_SCREEN',
            })
          ]
        })

this.props.navigation.dispatch(reset) ;

then this can support in nested stack navigator and tab navigator.

for now we need to do like below to handle nested navigator

const reset = NavigationActions.reset({
          index: 0,
          key: null,
          actions: [
            NavigationActions.navigate({
              routeName: 'HOME_SCREEN',
            }),
          ]
        })

   const profile =  NavigationActions.navigate({
              routeName: 'PROFILE_SCREEN',
            }),
   const setting   = NavigationActions.navigate({
              routeName: 'SETTING_SCREEN',
            })

this.props.navigation.dispatch(reset);
this.props.navigation.dispatch(profile);   
this.props.navigation.dispatch(setting);      

@kareem-adel
Copy link

I had an issue using reset with a nested screen, however you could navigate to the root route then from that route you could make a sub action to navigate to your nested route.

example:

this.props.navigation.dispatch(NavigationActions.reset({
            index: 0,
            key: null,
            actions: [NavigationActions.navigate({
                routeName: 'rootRoute',
                action: NavigationActions.navigate({routeName: 'subRootRoute'})
              })]
          }));

reference : https://reactnavigation.org/docs/navigation-actions.html#navigate (action)

@kkusanagi
Copy link

@kareem-adel had tried your solution but not exactly what I want.
only use stack navigator below. no tab navigator

Splash
Login
Main=>Home (as below)

Home
--Profile
--Setting
----Password

NavigationActions.navigate({
                routeName: 'Main',
                action: NavigationActions.navigate({routeName: 'Setting'})

if I use your solution to go to Setting page, you can't navigate back anymore. Setting page will be your homepage.

now my problem is how can I go to Password page in one line code.

@brentvatne
Copy link
Member

brentvatne commented Mar 6, 2018

the OP's problem is resolved using SwitchNavigator:

const AppNavigator = SwitchNavigator({
  Splash: { screen: SplashScreen },
  Unauthorized: { screen: UnauthorizedNavigator },
  Authorized: { screen: AuthorizedNavigator },
}, {
  initialRouteName: 'Splash',
  headerMode: 'none',
});
  • if you are on Splash and call this.props.navigation.navigate('Unauthorized') you will be brought to the welcome screen
  • If you call this.props.navigation.navigate('Authorized') from any screen in the Unauthorized stack, you will be brought to the initial route for Authorized.
  • if you call this.props.navigation.navigate('Unauthorized') you will be brought back to the Welcome screen (the initial route for Unauthorized).

you can read more about SwitchNavigator here: https://reactnavigation.org/docs/switch-navigator.html
and see an example of it in action here: https://reactnavigation.org/docs/auth-flow.html

if you have another issue that is similar to the one described by the OP but not solved by this, please create a new issue. thanks!

@react-navigation react-navigation locked and limited conversation to collaborators Mar 6, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests