native-navigation icon indicating copy to clipboard operation
native-navigation copied to clipboard

Feature Request: Navigator stack reset to <X> view

Open dzannotti opened this issue 8 years ago • 17 comments
trafficstars

I think an important navigation feature would be to reset the root navigation (this pattern is useful for apps that have a login screens, or an hamburger alike menu), in theory this could make issue #36 much more manageable (or defer the native drawer support to later).

dzannotti avatar Mar 19 '17 17:03 dzannotti

I just noticed that the native code does indeed have replace but it's not exposed to the JS module

dzannotti avatar Mar 19 '17 19:03 dzannotti

That is true, you could build one using NavigationActions.

https://reactnavigation.org/docs/navigators/navigation-prop#Reset

browniefed avatar Mar 19 '17 21:03 browniefed

@browniefed wrong project? xD

satya164 avatar Mar 19 '17 23:03 satya164

Oops haha

browniefed avatar Mar 19 '17 23:03 browniefed

haha

cyprusglobe avatar Mar 20 '17 18:03 cyprusglobe

@dzannotti we've been throwing around ideas for something like this, as a similar need arose internally.

I'd like to think more about how this would apply to drawer layout.... do you have a specific use case that you are wanting to use it for? I'd prefer to learn from real actual needs and try to make sure we solve those use cases as best as possible.

One idea we had thrown around is that we could have an option to present and push called clearUntil where you could pass it a route name and after presenting or pushing, it would clear the current navigation stack until the passed route name:

Navigator.push('ScreenA', props, { clearUntil: 'ScreenB' });

It's still unclear to me if this is the right model, and if it solves other people's use cases (which might be different than our own)

lelandrichardson avatar Mar 20 '17 20:03 lelandrichardson

@lelandrichardson the most common use case I can see for this is what happens when the user taps on an item of an hamburger menu where it's common to have a reset of the navigation stack and a display of a new route. This behaviour is also already available in the lib swift files but not exposed to js (https://github.com/airbnb/native-navigation/blob/master/lib/ios/native-navigation/ReactNavigation.swift#L170)

dzannotti avatar Mar 20 '17 23:03 dzannotti

Navigator.push('ScreenA', props, { clearUntil: 'ScreenB' });

@lelandrichardson clearUntil sounds more or less close to immediatelyResetStack from current js navigation solutions which is different than replace. Also, i feel like passing an option like that is confusing.

exposing Navigator.replace and Navigator.resetStack(maybe?) clearly communicates the intent here.

chirag04 avatar Mar 21 '17 14:03 chirag04

@chirag04 when would Navigator.resetStack(...) get called? before or after pushing a new screen? or at any moment? Would it be the responsibility of the pushed screen or the screen doing the pushing?

What animations do Navigator.replace(...) calls use?

I'm certainly amenable to adding a new top level call to Navigator, but I feel like I don't understand the semantics of replace yet enough to answer these basic questions...

lelandrichardson avatar Mar 21 '17 17:03 lelandrichardson

resetStack and replace can happen at any moment just like push. It will be the responsibility of screen doing the pushing. eg:

<Row
   title="Push new screen"
   onPress={() => Navigator.replace('ScreenTwo')}
/>

In above example, screenone gets replaced with screenTwo. replace uses no animation. replace is same as re-rendering screenone with a new set of config. It's just convenient to think of these two configs(with replace) as "screens"

chirag04 avatar Mar 21 '17 17:03 chirag04

To be clear, at airbnb the main use case is "success screens". Let's say we have a flow to have the user fill something out, and hit "submit". Once they've succeeded, we present them with a "Success" screen. At this point, they should not be able to go back, and when they hit dismiss, it should go back to before the flow started.

I feel like the right way to do this is to use present() and dismiss() but I think there's some nuance there in terms of what the user should see.

@chirag04 it sounds like your replace method is solving a fundamentally different use case, so perhaps we should be thinking of these things as two different features.

lelandrichardson avatar Mar 21 '17 17:03 lelandrichardson

To be clear, at airbnb the main use case is "success screens". Let's say we have a flow to have the user fill something out, and hit "submit". Once they've succeeded, we present them with a "Success" screen. At this point, they should not be able to go back, and when they hit dismiss, it should go back to before the flow started.

I feel like the right way to do this is to use present() and dismiss() but I think there's some nuance there in terms of what the user should see.

How would you do it if this success screen is not a modal on ios? if you use push then the back button should be able to take you to the previous screen(form). replace is the middle ground here.

chirag04 avatar Mar 21 '17 18:03 chirag04

I think the desired behavior in our case is as follows:

Let's say we have a stateful flow with 3 steps: A, B, and C. The user starts the flow from Home. We then have a success screen Success that we want to show after the user submits on C.

I think the desired navigation steps are:

  1. Home => A (push) (Home is reachable via pop)
  2. A => B (push) (A is reachable via pop)
  3. B => C (push) (B is reachable via pop)
  4. C => Success (push) (A, B, C, should no longer be reachable. pop/dismiss produces Home)
  5. Success => Home (dismiss)

This seems like a pretty different use case than what replace solves. So there may be two new APIs needed (replace, and some "clearing" ability somewhere).

lelandrichardson avatar Mar 21 '17 18:03 lelandrichardson

taking the same example:

Here are the navigation steps that i'm targeting:

  1. Home => A (push) (Home is reachable via pop)
  2. A => B (push) (A is reachable via pop)
  3. B => C (push) (B is reachable via pop)
  4. C => "D/success" (replace/no animation) (C, should no longer be reachable. pop/dismiss produces B)
  5. D/Success => B (dismiss)

I agree there is a need for two apis. 1) replace. 2) resetStack. Your use-case can be solved with resetStack. on step 4 you do resetStack(['Home', 'Success']);

Both these apis are very common in the js navigation solutions.

chirag04 avatar Mar 21 '17 19:03 chirag04

I see what you are saying i think, but why would C => Success not have an animation?

lelandrichardson avatar Mar 21 '17 20:03 lelandrichardson

when you have an animation like push, the expectation is that you should be able to go back to the previous screen. it depends on how you show the success screen. There are three ways to do it:

  1. C => success using push. push animation. expectation is you can go back to C.
  2. C => success using replace. No animation. no expectation of going back to C. back button points to B.
  3. C => success using present. modal animation. expectation here is debatable. I think it's fine to not land of C again here.

chirag04 avatar Mar 21 '17 20:03 chirag04

@lelandrichardson another good use case for exposing reset/replace stack is a classic login flow, after the login screen you'd never want to tap back to go to the login screen, but just replace the root component with a dashboard of some kind

dzannotti avatar Apr 06 '17 23:04 dzannotti