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

What should happen when passing params to a navigator route? #43

Open
brentvatne opened this issue May 7, 2018 · 14 comments
Open

What should happen when passing params to a navigator route? #43

brentvatne opened this issue May 7, 2018 · 14 comments

Comments

@brentvatne
Copy link
Member

brentvatne commented May 7, 2018

  • Should they be passed down into the active child route?
  • Should they be passed down into all child routes?
  • Should they be exposed through getParentParam or something?

Related issues:

@liuqiang1357
Copy link

liuqiang1357 commented May 27, 2018

I don't think the getParentParam is necessary, because if we passed params to child navigator, we can reference the params by dangerouslyGetParent().getParams in the child screen, and we also can wrap the navigator to decide how to use the params for all the child screen (see pull request below).

But at first we should let the child navigator to receive params. There is some issue in current version and I have made a pull request #4306 .

@mtnt
Copy link

mtnt commented Jun 6, 2018

Since it is react-navigation I guess params should be converted to props. It should be passed to a target component only. All further props-params manipulations in the component is up to a developer.

Since I believe the navigator should be able to made as a HOC (without any scene changed inside itself), params should be available to be taken in navigationOptions, for example, for a header configuration.

@brentvatne
Copy link
Member Author

brentvatne commented Jun 7, 2018

@mtnt - they are props. this.props.navigation.state.params. that's not really what this issue is about though. the question here is how we should deal with params when they are passed into a navigator, so if you navigate to a screen that is just a react component vs a screen that is a navigator, would params be passed through to the screen rendered by the navigator or not?

@mtnt
Copy link

mtnt commented Jun 7, 2018

@brentvatne I meant first level props - this.props.param0, but ok, I`ll open another one issue for this point.

About your comment. As I already said, I think params should be passed into a navigation target screen only (without passing through). What to do with it further is the screen responsibility.

@brentvatne
Copy link
Member Author

this causes a lot of confusion - react-navigation/react-navigation#4811

we should document this.props.navigation.dangerouslyGetParent().getParam(...) as a way to get the params from the parent, or provide a method that gets params from the parent without the word dangerously maybe...

@Titozzz
Copy link

Titozzz commented Mar 6, 2019

I have a use case where It would be helpful to me that any screen / navigator under a certain navigator gets a common param, and I really believed this would be the default behavior (passing down all params with the child always having priority when param names conflict).

I will use a workaround by extending navigator and using react context in the meantime 😄

@slorber
Copy link
Member

slorber commented Mar 6, 2019

Hi @Titozzz

If you need to build some kind of multiscreen stateful wizzard you can share state across those screens by wrapping the navigator.

MyStatelessStackNavigator = createStackNavigator({})

// Forward the prop
MyStatefulStackNavigator = ({navigation}) => (
   <MyStateProvider>
      <SomeFancyWizzardStepHeader/>
      <MyStatelessStackNavigator navigation={navigation}/>
   </MyStateProvider>
);

// Very important otherwise MyStatefulStackNavigator is not considered as a navigator
MyStatefulStackNavigator.router =  MyStatelessStackNavigator.router; 

export default MyStatefulStackNavigator;

This is a common pattern where you just wrap a navigator inside custom one to enhance it.
More infos here: https://reactnavigation.org/docs/en/custom-navigators.html

Unlike global state based solutions, this one will actually reset its state when you navigate away from this navigator, which makes it more suitable for transcient state like multi-screen forms/wizzards... I think we should do a better job at documenting this kind of patterns with react-navigation. Will probably write a blog about it soon.

Note you might be tempted to try to use "screenProps" here but actually this should only be used on root app container, and after discussing with Satya last year he suggested using context is the way to go for this kind of usecase, and maybe screenProps would be deprecated some day.

Note if you are looking for an easy to use state provider to make your stack navigation stateful, I think unstated is a very decent option and anyone knowing React will understand how to build features with this very easily. Once it's setup you almost get no learning curve. Only difference is setState being async (which imho is a nice addition).

And if you don't need a stateful wrapper, you can still use the trick above and add a simple React context to propagate a static value to all subscreens.

@Titozzz
Copy link

Titozzz commented Mar 6, 2019

Using react context wrapping my navigator is exactly what I did, sorry I did not give more details in my above message 😄. And once hooks land in stable react-native it'll get even cleaner to use.

@bhave-abhay
Copy link

bhave-abhay commented Mar 9, 2019

I was struggling with the same, and after almost a week of frustration finally used context and got what I wanted, demonstrated in react-navigation/react-navigation#5646

I think the mention of possibilities with the use of cotext api (though it is in parent platform i.e. react) in react-navigation documentation would help a lot.

Should we / I open an issue for this on documentation site repo?

@brentvatne
Copy link
Member Author

@bhave-abhay - a docs page for that could be really great! you would probably be a great person to write it given the experience you've had, can you open a pr for it?

richeterre added a commit to richeterre/jumu-app that referenced this issue Jul 30, 2019
Instead of passing the selected contest as a nav param to the nested navigation structure (tab + stack navigators), which is tricky because params don't propagate to child navigators (see react-navigation/rfcs#43 and react-navigation/rfcs#68), we wrap the navigator structure with a ContestNavigator that receives the current contest as a prop, and passes it forward to its navigation screens as a param. The RootView component tracks the current contest and renders either a ContestNavigator or (if no contest selected) a contest picker.
@SaeedZhiany
Copy link

@brentvatne
Let's assume a routing like below:

DrawerNavigator:
|______ StackNavigator
          |______ Screen 1
          |______ TabNavigator
                    |_______ Tab 1 <--- initial route
                    |_______ Tab 2
                    |_______ Tab 3

One scenario for the routing is an app that shows a list of items in Screen 1 and shows an item's detail when the user pressed on the item. because of the massive amount of information, the developer decides to show the information in three tabs (price tab, technical info, and contact tab), So the tabs are related, also, assume that the developer should fetch the data by sending 3 different requests to a server (in the scenario I'm thinking about the tabs are able to work completely separated from each other, in other words, although the tabs are showing information of the same Item all of their processes are isolated from each other). in the other hand, the server may expect an ItemId and some other parameters in the request for each tab.

in the current implementation, we should pass all parameters to the TabNavigator and extract the required params in each tab using this.props.navigation.state.params in Tab 1 and this.props.navigation.dangerouslyGetParent().getParams in the other Tabs. it has two cons in my opinion:

  1. If the developer wants to change the order of the tabs, he/she must change the code to retrieve params, because of two different access way to the parameters.
  2. Developers like me may like to convert params to component props to easily accessible in a consistent way (by using something like below)
Tab 1: {
    screen: (props) => (<ScreenWrapper component={ItemOverviewScreen} params={props}/>)
}

Class ScreenWrapper {
    public render() {
        const Component = this.props.component;
        return (
            <Component {...this.props.params}/>
        );
    }
}

Class ItemOverviewScreen {
    // work with props without any concern and knowledge about routings parameter passing
}

Not passing down the params causes we lose the above ability in tab navigators at least.

  1. For some reason, Let assume that the app's routing is changed like below:
DrawerNavigator:
|______ StackNavigator
          |______ Screen 1
          |______ TabNavigator
                    |_______ Tab 1 <--- initial route
                    |_______ Tab 2
                    |_______ Tab 3
          |______ Tab 3 <--- the same screen used in above Tab 3

the developer must check both this.props.navigation.state.params and this.props.navigation.dangerouslyGetParent().getParams to cover both routing in one place.

My suggestion
Passing the params of each tab by wrapping them into an object with a key of the corresponding Tab routing name. I mean something like below:

The developer doing navigate to Tab Navigator:

NavigationAction.navigate ({
    routeName: TabNavigator,
    params: {
        Tab 1: {
            // params of Tab 1
        },
        Tab 2: {
            // params of Tab 2
        },
        Tab 3: {
            // params of Tab 3
        },
    }
});

the library should pass only the corresponding params to each Tab in the screen: (props) => ....
probably we can extend the suggestion for other Navigators include nested ones.

@VBarzionov
Copy link

I need to pass same "userId" param to all Tabs inside Tab.Navigator
Doc says there is no "dangerouslyGetParent().getParams" in React Navigation 5 any more.
However it is totally unclear how to share/access same "nav-param" for all Tabs inside TabNavigator now.
One solution is using external storage (react-context/redux/other).

I found following solution/workaround. Hope it helps somebody.

  <Stack.Screen name="Settings" >
    { (props) => (
      <Tab.Navigator>
        <Tab.Screen name="Tab1" component={ Tab1 } 
          initialParams={ props.route.params }  // <- pass params from root navigator
        />
        <Tab.Screen name="Tab2" component={ Tab2 } 
          initialParams={ props.route.params } 
        />
      </Tab.Navigator>
      )}
  </Stack.Screen>

Navigation params pass from root stack-navigator to all Tabs

@ehxxn
Copy link

ehxxn commented Jul 23, 2020

@brentvatne getParam is undefined

@imavipatel
Copy link

I need to pass same "userId" param to all Tabs inside Tab.Navigator
Doc says there is no "dangerouslyGetParent().getParams" in React Navigation 5 any more.
However it is totally unclear how to share/access same "nav-param" for all Tabs inside TabNavigator now.
One solution is using external storage (react-context/redux/other).

I found following solution/workaround. Hope it helps somebody.

  <Stack.Screen name="Settings" >
    { (props) => (
      <Tab.Navigator>
        <Tab.Screen name="Tab1" component={ Tab1 } 
          initialParams={ props.route.params }  // <- pass params from root navigator
        />
        <Tab.Screen name="Tab2" component={ Tab2 } 
          initialParams={ props.route.params } 
        />
      </Tab.Navigator>
      )}
  </Stack.Screen>

Navigation params pass from root stack-navigator to all Tabs

thank you so much for this. I was trying to solve this problem from last 2 days luckily i reached here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants