react-navigation / rfcs

RFCs for changes to React Navigation
Other
88 stars 16 forks source link

how to send navigation events between peer navigators? #78

Open matthargett opened 5 years ago

matthargett commented 5 years ago

We have a main screen with a tab navigator, and a pulldown sheet that also has a tab navigator. The navigators are peers/siblings in the top-level App.js.

image

In this example, the user gets a chat message in the pulldown sheet that contains a link (green triangle) that triggers a navigation in the main screen's tab navigator. Conversely, the main window can contain content (blue square link) that triggers a navigation in the pulldown sheet's tab navigator.

Some ideas we have thought about

  1. Using a ref, as documented here: https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
  2. Opening a deep link on our own app, which also helps e2e test automation a bit, but potentially creates security issues.
  3. Writing a custom navigator, which we don't want to do if we can help it due to the maintenance burden.
  4. Don't use react-navigation for the pulldown sheet, and use a custom (re)Animated and native navigation solution. We really don't want to do this if we can help it due to the maintenance burden, and losing the herd immunity of community-hardened code in react-nav.

This seems possible given all the atoms available in react-nav and animted-bottom-sheet, but we're not sure what the right thing to do is that will have good looking transitions and perform well on lower-end Android.

ericvicenti commented 5 years ago

I'm a bit confused how each of these navigators relate to each other? A typical react-nav app will have one top-level navigator, owned by a container like createAppContainer. If you want to link from one navigator to another, you'd put both of them under the top level navigator, and navigate actions will be negotiated amongst them.

Your example shows two tab navigators, but without knowing how they are set up, its hard to advise you.

In the perfect world, I think both tab navigators would be owned by a common parent navigator with a switch/tab router to perform the logic, and a custom view to present the pulldown.

alo0822 commented 5 years ago

@ericvicenti Thanks for the response. We're trying to figure out the best way to set up the two tab navigators while still being able to have the necessary interactions and navigation. There are two main issues we're trying to solve:

  1. The pulldown sheet must be draggable, similar to the iOS notification center.
  2. We must be able to navigate from one tab navigator to the other and vice versa (as illustrated in the original comment).

If the two tab navigators are owned by a common parent navigator with a switch/tab router, I'm not sure how to also implement this dragging interaction.

This is what we've tried so far. Each of the tab navigators is owned by a container like createAppContainer, which allows us to implement the dragging interaction. However, this creates two separate navigation states. To solve this, we pass refs for each of the navigators to NavigationService and use that to access one navigator from the other.

I understand that a typical react-nav app should only have one app container, so although this method works for what we need to do, is this going against best practices for react-navigation? Are there any other issues with explicitly rendering more than one navigator, besides that they won't be able to interact with each other?

alo0822 commented 5 years ago

Following up on my last comment with some more details and updates.

We currently have two separate tab navigators, one for the main screen and one for the pulldown view, each owned by a container. Then, we pass refs for each of the navigators to a navigation service in order to access one navigator from the other (following this documentation).

// App.js
import { createBottomTabNavigator, createAppContainer } from 'react-navigation';
import MainScreenNavigationService from './MainScreenNavigationService';
import PulldownNavigationService from './PulldownNavigationService';

const MainScreenNavigator = createAppContainer(createBottomTabNavigator(...));
const PulldownNavigator = createAppContainer(createBottomTabNavigator(...));

export default class App extends React.Component {
  // ...

  render() {
    return (
      <MainScreenNavigator
        ref={navigatorRef => {
          MainScreenNavigationService.setTopLevelNavigator(navigatorRef);
        }}
      />
      <PulldownNavigator
        ref={navigatorRef => {
          PulldownNavigationService.setTopLevelNavigator(navigatorRef);
        }}
      />
    );
  }
}

From the Common Mistakes page, we know that explicitly rendering more than one navigator will cause the navigators to have separate navigation states, so they can't interact with each other. We've tried to solve this problem by using the navigation service method as mentioned above.

What we'd like to further understand is:

  1. Are there any other issues with explicitly rendering more than one navigator, besides the navigators not being able to interact?
  2. Are there any issues with solving this problem using the navigation service method as mentioned above?
ericvicenti commented 5 years ago

createAppContainer is not meant to be used twice in one app. The main problem is that both containers will do things that only should happen once at the top level, like subscribe to BackHandler and handle link events from RN's Linking module. This app would probably have bugs where both navigators attempt to handle the android back button, and both would attempt to handle a url link.

By attempting to use independent services for each navigator, you undo a bunch of the value that React-Nav provides, such as allowing you to link across navigators.

It sounds like you want to fork createAppContainer, if you have a need to support multiple independent navigation subtrees in your app.