Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change Bottom Tab Bar based on state in React Navigation with navigationOptions

I want to change the bottom tabs on the screen based on what features are enabled. This feature list is populated via a login API call.

Currently I have the following:

const TabRouteConfig = {
  Home: DashboardScreen,
  FeatureA: FeatureA,
  FeatureZ: FeatureZ,
};

const TabNavigatorConfig = {
  initialRouteName: 'Home',
  order: [
    'Home',
    'Feature A',
  ],
  tabBarPosition: 'bottom',
  lazy: true,
};

const TabNavigator1 = createBottomTabNavigator(
  TabRouteConfig,
  TabNavigatorConfig,
);

// Set the tab header title from selected route
// https://reactnavigation.org/docs/en/navigation-options-resolution.html#a-stack-contains-a-tab-navigator-and-you-want-to-set-the-title-on-the-stack-header
TabNavigator1.navigationOptions = ({ navigation }) => {
  const { index, routes } = navigation.state;
  const { routeName } = routes[index];

  return {
    headerTitle: routeName,
  };
};

const TabNavigator2 = createBottomTabNavigator(
  TabRouteConfig,
  {
   ...TabNavigatorConfig,
   order: [
      'Home',
      'Feature Z',
   ]

);

TabNavigator2.navigationOptions = TabNavigator1.navigationOptions;

const Stack = createStackNavigator({
  Main: {
    screen: props => (props.screenProps.hasFeature ?
      <TabNavigator1 /> : <TabNavigator2 />
    )
  },
})

const WrappedStack = props => (
  <View style={styles.container}>
    <Stack
      screenProps={{ hasFeature: props.hasFeature }}
    />
  </View>
);

const mapStateToProps = (state, props) => {
  return {
    ...props,
    hasFeature: state.hasFeature,
  };
};

export default connect(mapStateToProps, null)(WrappedStack);

This mostly works - it dynamically switches between TabNavigator1 and TabNavigator2 based on hasFeature, but it no longer honors the navigationOptions placed on the TabNavigators and the headerTitle is not set.

Is there a better way to do this?

like image 410
sguha Avatar asked Sep 19 '18 19:09

sguha


People also ask

How do I customize the tab bar in react navigation?

Add icons to the tab bar To add icons to each tab, first import the Icon component from react-native-vector-icons library inside the navigation/TabNavigator/index. js file. For this example, let's use AntDesign based icons. // after other import statements import Icon from 'react-native-vector-icons/AntDesign';

How do I use tab navigation with stack navigation?

Tab navigator nested inside the initial screen of stack navigator - New screens cover the tab bar when you push them. Drawer navigator nested inside the initial screen of stack navigator with the initial screen's stack header hidden - The drawer can only be opened from the first screen of the stack.


1 Answers

It's an antipattern to render more than one navigator simultaneously as the navigation states of those navigators will be completely separated, and you will not be able to navigate to one from another.

You can use tabBarComponent option to achieve what you want. Hope you can get the idea from below example:

import { createBottomTabNavigator, BottomTabBar } from 'react-navigation-tabs';

const TabNavigator = createBottomTabNavigator(
  TabRouteConfig,
  {
    tabBarComponent: ({ navigation, ...rest }) => {
      const filteredTabNavigatorRoutes = navigation.state.routes.filter(route => isRouteAllowed(route));
      return (
        <BottomTabBar
          {...rest}
          navigation={{
            ...navigation,
            state: { ...navigation.state, routes: filteredTabNavigatorRoutes },
          }}
        />
      );
    },
  },
);

NOTES:

  • You don't have to install react-navigation-tabs separately. It is automatically installed with react-navigation 2.0.0+.
  • isRouteAllowed is the function which returns true or false based on whether to show that route or not. Make sure to only check the top level routes in that object.
  • TabRouteConfig should contain all possible tabs, and this logic only hides the route from the TabBar visually. So, you can still programmatically navigate to all routes. Therefore, you might need additional logic in each of those screens to decide whether to render them based on hasFeature.
like image 123
vahissan Avatar answered Oct 06 '22 00:10

vahissan