6

I've got a React Native app with React Navigation and I need to show or hide a tab based on Redux state. I'm using createBottomTabNavigator and have written this:

function createTabNavigator(props:TabNavigatorProps){
  debugger;
  const isClient = WHAT_TO_PUT_HERE??
  const tabs = isClient
  ? {
   //something
  } 
  : {
    //something else
  } 
  return createBottomTabNavigator(
   tabs,
    {
      defaultNavigationOptions: ({ navigation }) => ({
        tabBarIcon: ... ,
        tabBarLabel: ...,
      }
      tabBarComponent: (props) => customTabBar(props, isClient),
      tabBarOptions: {
        ...
      },
    });
};

My createTabNavigator works the way as intended if I manually set isClient to true or false, though I need this from redux state.

I've also tried connecting it, and mapStateToProps get called with the correct state, however it doesn't call createTabNavigator again so state changes aren't updated to my view hierarchy.

How can I add/remove a tab (and actually re-render altogether as that tab also involves some custom styling/rendering of the whole bar) based on Redux state?

Can Poyrazoğlu
  • 33,241
  • 48
  • 191
  • 389
  • Cannot you connect your `customTabBar` to redux (imaging it's a different component) and making it aware about the redux current state? – Auticcat Aug 28 '19 at 15:16
  • @Auticcat nope, because the tabs themselves are dynamic and are defined in `createTabNavigator` and passed to `createBottomTabNavigator` directly (and `tabs` change depending on the redux state), whereas `tabBarComponent` is a component on its own that doesn't really "know" the tabs (it just checks `isClient` and adds some visual decoration around the tab bar by wrapping it, not touching the internals of the actual tab bar). – Can Poyrazoğlu Aug 29 '19 at 07:28

3 Answers3

2

react-navigation does not provide any API to hide a tab from BottomTabNavigator.

However, it allows providing custom TabBarComponent. It also exposes BottomTabBar component which is the default TabBarComponent for BottomTabNavigator.

This BottomTabBar component takes a property getButtonComponent to get the component for rendering each tab. It is provided the tab details in argument and is expected to return component to be used for rendering that tab.

To achieve hiding of tabs dynamically, you provide your own function for getButtonComponent. Check the details of tab with your state to decide whether it should be visible or hidden. Return an 'empty component' if you want to hide it, else simply call the default.

/**** Custom TabBarComponent ****/
const BlackHole = (props) => []
let _TabBarComponent = (props) => {
  const getButtonComponent = (arg) => {
    const hide = props.hiddenTabs[arg.route.key];
    if (hide) {
      return BlackHole;
    }
    return props.getButtonComponent(arg)
  }
  return (<BottomTabBar {...props} getButtonComponent={getButtonComponent}/>);
};

const mapStateToProps = (state) => ({
  hiddenTabs: state.hiddenTabs
});

const TabBarComponent = connect(mapStateToProps)(_TabBarComponent);

/**** Navigation ****/
const TabNavigator = createBottomTabNavigator({
  Tab1: createStackNavigator({Screen1}),
  Tab2: createStackNavigator({Screen2}),
  Tab3: createStackNavigator({Screen3}),
}, {
  tabBarComponent: props => (
    <TabBarComponent {...props} style={{borderTopColor: '#605F60'}}/>
  )
});

See this snack for POC: https://snack.expo.io/BJ80uiJUH

ckedar
  • 1,859
  • 4
  • 7
0

We had the same requirement but in react navigation we cannot have dynamic tab, as we have to define all the routes statically well in advance.

Check this answer, in which I have explained in detail about how we achieved it.

Also have mentioned other possible approach (which we have not done).

There is one more interesting article you might want to check on this.

Gokul Kulkarni
  • 2,069
  • 3
  • 26
  • 42
0

Make sure you are not calling redux state inside a HOC Component , For state management ,you can try to render it inside a component , try this logic :

class TabIndex extends Component {
        render() {
             // coming from redux
            let isClient= this.props.isClient;

        return (<View>
                {isClient?<TabIndexNavigator ifYouNeed={ isClient } />
                 :
                null}
                </View>)
    }
 }

module.exports = TabIndex

then import it normally in your main stackNavigator

const StackMain = StackNavigator(
    {
        TabIndex: {
            screen: TabIndex
        },
        ...
}
Mustafa Tawfig
  • 353
  • 1
  • 3
  • 9