React Navigation v5: How to get route params of the parent navigator inside the child screen
Asked Answered
T

4

8

So I have nested navigators

Main BottomTab.Navigator

  • Profile Bottom Tab # 1 (Stack.Navigator)
    • Profile View (Screen)
    • Followers (Screen)
    • Following (Top Tab.Navigator)
      • Pages (Screen)
      • Groups (Screen)
  • Feed Bottom Tab # 2 (Stack)
  • Some other Bottom Tab # 3 (Stack)

The problem is when I navigate from Profile View Screen to Following Navigator, I pass some parameters to the parent Following Navigator and I want all of those params in the children tabs Screens (Pages/Groups).

But the route of the children tab screens do not get the parameters which are passed to the parent navigator (Following Tab Navigator)

Is there a way to do that?

Here's my code: Profile Stack

const ProfileStack = () => (
  <Stack.Navigator
    initialRouteName='profileView'
  >
    <Stack.Screen
      name='profileView'
      component={ProfileScreen}
      options={{
        headerMode: 'screen',
        headerShown: false,
      }}
    />

    <Stack.Screen
      name='followers'
      component={FollowersScreen}
      options={{
        headerTitle: 'Followers',
      }}
    />
    <Stack.Screen
      name='following'
      component={FollowingTabs}
      options={{
        headerTitle: 'Following',
      }}
    />
 </Stack.Navigator>

FollowingTabs

const Tabs = createMaterialTopTabNavigator();
export const FollowingTabs = () => (
  <Tabs.Navigator
    initialRouteName='page'
    lazy
    swipeEnabled
  >
    <Tabs.Screen
      component={PageScreen}
      name='page'
      options={{ tabBarLabel: '2 Pages' }}
    />
    <Tabs.Screen
      component={GroupScreen}
      name='groups'
      options={{ tabBarLabel: '3 Groups' }}
    />
  </Tabs.Navigator>
);

From the profileView Screen I'm trying to navigate to the following Tabs Screen and need to pass some parameters as follows.

const onPressHandler = () => {
    navigation.navigate('following', **{ isPublicProfile, firstName }**);  // These parameters are passed to the route of the following Tabs Navigator
  };

And when I try to read these parameters in children tabs (Pages/Groups) these are undefined

const PageScreen = ({ route }) => {
  const { isPublicProfile, firstName } = route.params; // undefined?? Can't read parent's params
...

Any help would be appreciated.

Edit: I found this open Issue on GitHub (https://github.com/react-navigation/rfcs/issues/43) Is this not possible yet?

Towandatoward answered 9/4, 2020 at 17:49 Comment(0)
T
12

So I ended up use React.Context as recommended by official React Navigation documentation. Please Follow the official documentation for more information.

1- Use React context and wrap the navigator with a context provider to pass data to the screens (recommended).

Here's my solution:

const DEFAULT_CONTEXT = {
  isPublicProfile: false,
  ...
};

const FollowingTabNavigatorContext = createContext(DEFAULT_CONTEXT);

In Parent Following Tab Navigator

const Tabs = createMaterialTopTabNavigator();

export const FollowingTabs = ({ route }) => {
  const { isPublicProfile } = route.params;
  return (
    <FollowingTabNavigatorContext.Provider value={{ isPublicProfile }}>
      <Tabs.Navigator
        initialRouteName='pages'
        lazy
        swipeEnabled
      >
        <Tabs.Screen
          component={PageScreen}
          name='pages'
          options={{ tabBarLabel: '2 Pages' }}
        />
        <Tabs.Screen
          component={GroupScreen}
          name='groups'
          options={{ tabBarLabel: '3 Groups' }}
        />
      </Tabs.Navigator>
    </FollowingTabNavigatorContext.Provider>
  );
};

FollowingTabs.propTypes = propTypes;
FollowingTabs.defaultProps = defaultProps;

And Finally In my the child Tab Screens:

export const GroupScreen = () => {
  const { isPublicProfile } = useContext(FollowingTabNavigatorContext);
  ...
}
Towandatoward answered 10/4, 2020 at 11:36 Comment(0)
M
4

You can easily use initialParams in tab navigator and pass the props. Check this solution.

<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>

Reference https://github.com/react-navigation/rfcs/issues/43#issuecomment-610706264

Mealie answered 4/8, 2021 at 12:25 Comment(1)
This will get overwritten if any of the nested screens need their own parameters. Not an ideal approachBerchtesgaden
T
3

So Here's an alternate hacky solution without using context ;p

BUT Beware this render callback solution comes with a cost. Read here

Note: By default, React Navigation applies optimizations to screen components to prevent unnecessary renders. Using a render callback removes those optimizations. So if you use a render callback, you'll need to ensure that you use React.memo or React.PureComponent for your screen components to avoid performance issues.

const Tabs = createMaterialTopTabNavigator();

export const FollowingTabs = ({ route }) => {
  const { isPublicProfile } = route.params;
  return (
      <Tabs.Navigator
        initialRouteName='pages'
        lazy
        swipeEnabled
      >
        <Tabs.Screen
          name='pages'
          options={{ tabBarLabel: '2 Pages' }}
        >
          {() => <PageScreen isPublicProfile={isPublicProfile} />}
        </Tabs.Screen>
        <Tabs.Screen
          name='groups'
          options={{ tabBarLabel: '3 Groups' }}
        >
         {() => <GroupScreen isPublicProfile={isPublicProfile} />}
        </Tabs.Screen>
      </Tabs.Navigator>
  );
};

FollowingTabs.propTypes = propTypes;
FollowingTabs.defaultProps = defaultProps;
Towandatoward answered 25/6, 2020 at 7:11 Comment(0)
A
1

Edit!!! : This only works the first time you navigate to the child object. There's probably a way to reset the child or parent every time, but using context is probably better...

If anyone is still wanting a non-context solution, I believe you can use the initialParams prop in the children to do what you're looking for.

Assume route has some params passed to it.

export const FollowingTabs = ({route}) => (
  <Tabs.Navigator
    initialRouteName='page'
    lazy
    swipeEnabled
  >
    <Tabs.Screen
      component={PageScreen}
      name='page'
      options={{ tabBarLabel: '2 Pages' }}
      initialParams={route.params // Then you can grab these params using the usual route.params in the PageScreen component} 
    />
    <Tabs.Screen
      component={GroupScreen}
      name='groups'
      options={{ tabBarLabel: '3 Groups' }}
    />
  </Tabs.Navigator>
);
Archfiend answered 24/5, 2021 at 16:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.