wix / react-native-navigation

A complete native navigation solution for React Native
https://wix.github.io/react-native-navigation/
MIT License
13.04k stars 2.67k forks source link

Proposal: Ability to get navigation screens state #6853

Open Natteke opened 3 years ago

Natteke commented 3 years ago

🚀 Feature

Ability to get navigation screens state, with stacks, components, props and options.

Have you read the Contributing Guidelines on issues?

Yes

Motivation

Example

imagine a Chat app which contains ChatsListScreen, NewChatScreen and ChatScreen itself. The user can navigate ChatsListScreen <-> ChatScreen, which makes perfect sense. But what if the user goes from ChatsListScreen -> NewChatScreen and selects a user to create a new chat with? The user then lands in the ChatsScreen as expected, but should not go back to the NewChatScreen when the back button is pressed. In this case you would want to reflect the navigation state and check out if there is a NewChatScreen "in the middle" so you can directly pop to the ChatsListScreen instead

Pitch

From the user's point of view, I think it would be logical to make a method to get the state of navigation. const state = Navigation.getState();

It would also not be superfluous to be able to subscribe to its change.

const { unsubscribe } = Navigation.addStateChangeListener(callback); // unsubscribe from corresponding code
Navigation.removeStateChangeListener(callback); // unsubscribe from anywhere else

I deliberately will not suggest a data structure to represent the state. Instead, I will describe what it should contain:

mrousavy commented 3 years ago

I like that idea. I'm not exactly sure why we don't expose a state already (@guyca can you fill us in here?), but I can imagine the data structure in which the stack should be represented can get quite complicated. How do you display the following:

  1. Set Root to Stack Layout
  2. Push Screen A
  3. Push Screen B
  4. Show Screen C as a full screen modal (with stack layout inside)
  5. Push Screen D inside Screen C

Would we just show a queue in this case and treat modals as full-screen screens?

Natteke commented 3 years ago

I will leave my examples where the presence of such storage was help me a lot in a real application

1) When opening a push notification that should take the user straight to some screen, knowing which screen is at the top prevents me from opening it again.

Case:

2) By design I have a custom header. It has a back button with arrow/cross icon. This button looks at the store, and understands what it should do: -If this is the last screen in the modal stack, it closes the modal -if its not the last one , it does pop -its decide icon: x-mark/arrowLeft

3) Business logic with the behavior of the application, depending on which path it took to get to the screen

3.5) it was handy to make breadcrumbs with this store

And these are just my cases

jinshin1013 commented 3 years ago

@mrousavy @Natteke Exposing the navigation state is such a great idea! We have tried multiple different solutions over time but never really settled on any concrete pattern. Have you guys got any suggestion in how to actually expose these states across all registered screens? In the past, we have experimented with React Context as you can see in the documentation however there are some known limitations such as state changes not triggering the re-rendering on all registered screens as they are on a separate component tree. However solutions that utilise Proxies (we simply used MobX) can be used to combat this limitation.

mateioprea commented 3 years ago

What about using an actual Stack data structure natively and update that stack based on what the user do? It makes sense since once you pop anything from your screens it will be reflected in that stack.

Then just offer a native event that you can subscribe to, to query the stack state.

mrousavy commented 3 years ago

@mateioprea I feel like there's no reason to do this natively, as that results in extra & unnecessary bridge traffic. We can do this in JS as an actual "queue" and on changes we raise events 🤷‍♂️

a-eid commented 3 years ago

this would allow setting the theme dynamically without needing to reset the navigation, correct ?

yogevbd commented 3 years ago

@Natteke

Example

imagine a Chat app which contains ChatsListScreen, NewChatScreen and ChatScreen itself. The user can navigate ChatsListScreen <-> ChatScreen, which makes perfect sense. But what if the user goes from ChatsListScreen -> NewChatScreen and selects a user to create a new chat with? The user then lands in the ChatsScreen as expected, but should not go back to the NewChatScreen when the back button is pressed. In this case you would want to reflect the navigation state and check out if there is a NewChatScreen "in the middle" so you can directly pop to the ChatsListScreen instead

I think there is something wrong in the layout you described, you should open the NewChatScreen as a modal, it's not actually related to the chat stack. I would first suggest to open NewChatScreen as a modal, when the user finish's creating the new chat, dismiss the modal and push the ChatScreen normally to the stack (See What'sApp implementation).

I'm not sure about this feature because most times you need to know how the layout hierarchy look like, it means something is wrong with the usage or the hierarchy itself.

Let's try to find more use cases where we need it.

Natteke commented 3 years ago

@yogevbd, this example was provided by @mrousavy. He edited the issue. My reall app use cases are in comment.

But i think i will gather more from my colleagues next week. Also i will try to figure out a data stucture to represent the store. So far, on draft, it looks complicated

Natteke commented 3 years ago

this would allow setting the theme dynamically without needing to reset the navigation, correct ?

@a-eid, in this proposal I only brought up the question of getting detailed information about the current state of the navigation/screens.

phepsi commented 3 years ago

I bumped into this thread while googling about this very same thing.

In our app the issue is that we have lots of logic written into sagas and in background we may have notifications, disruption messages etc. coming in and in some screens we don't want to show them (e.g. when user is making a payment), so basically we are trying to track down the navigation state using appear/disappear-events and the logic deciding when to show all those messages. This is working okay, but it's quite cumbersome and may easily lead into situation where app state doesn't match with the actual state (and that pretty much blows up everything). Having some way to get the actual state from RNN would be awesome feature!

maxp-hover commented 3 years ago

It seems like there's a bug when you navigate to a root page that is already the current root page. It won't pass the props correctly. We need to temporarily navigate to some other page in order for it to work. Having the ability to see the current page would let us know when to use updateProps instead. Since we can't do that, we have to do some hacky workaround (rendering a dummy page first). So, this feature would be appreciated.

Our use case for this, is when our application is opened with an emailed link. If the application is minimized, and then the link re-clicked, it won't go to the page correctly like it did the first time.

phepsi commented 3 years ago

I bumped into this thread while googling about this very same thing.

In our app the issue is that we have lots of logic written into sagas and in background we may have notifications, disruption messages etc. coming in and in some screens we don't want to show them (e.g. when user is making a payment), so basically we are trying to track down the navigation state using appear/disappear-events and the logic deciding when to show all those messages. This is working okay, but it's quite cumbersome and may easily lead into situation where app state doesn't match with the actual state (and that pretty much blows up everything). Having some way to get the actual state from RNN would be awesome feature!

To continue with this, right now I'm trying to update RNN from 7.8.4 to something newer, but it seems that appear/disappear-events have changed somehow on Android which messes up our navigation state tracking. So yeah, it's very fragile and I would love to get rid of it.

habovh commented 3 years ago

I'd like to add an example that would benefit from this kind of improvement: logging.

When calling Navigation.pushScreen(), I could log a "navigation breadcrumb" including the source component name, the source props, the destination component name and the destination props, using only the source component ID and the pushScreen() original signature parameters.

givehug commented 3 years ago

Yes please !

Joxter commented 2 years ago

Yes please!

ryanliljestrom commented 2 years ago

Adding an additional scenario where this would be extremely helpful: SplitView. Very frequently when navigating it helps to know if we are in a SplitView, and if so what is the ID of our "sibling" component