somnathbm
somnathbm

Reputation: 659

How to properly use TabNavigator inside another React Component

Question - How to properly use React Navigation's TabNavigator container component inside another React component that acts just as wrapper component?

What I want to achieve - Basically I want the appbar and tabbar both to be displayed - appbar on the top, tabbar (TabBarTop) just beneath it, a very common design pattern.

I have tried a couple of ways.

Method #1 (Nesting inside StackNavigator)

Tab.js

export const Tab = TabNavigator({
    Tab1: { screen: Tab1Container },
    Tab2: { screen: Tab2Container }
  }, {
    tabBarComponent: TapBarTop,
    tabBarPosition: 'top'
  });

AppBar.js

class AppBarComponent extends Component {
  static navigationOptions = { header: null }
  render() {
    return (
      <View>
        *some more views, buttons blah blah here
      </View>
    )
  }
}
export default AppBarComponent;

and I use them inside StackNavigator, like

export default StackNavigator({
  stack1: { screen: AppBarComponent },
  stack2: { screen: Tab }
});

This results in only 1 stack to be displayed at a given time which is exactly how it works. And I don't have anything to do with initialRouteName.

Method #2 (wrapping inside another component)

<View style={{ flex: 1 }}>
  <AppBarComponent />
  <Tab />
</View>

This on the contrary displays both components but this.props.navigation.navigate('somepath') or push() or pop() or replace() doesn't work from inside and . But this.props.navigation and its methods all are available inside those components.

PS - I'm using React Navigation v1 and running on iOS

Upvotes: 4

Views: 3521

Answers (2)

somnathbm
somnathbm

Reputation: 659

@bennygenel your answer is definitely useful in addressing the problem. I kind of fed wrong information when I talk about having components inside TabNavigator. Well, instead of dumb components, there were multiple redux containers inside the TabNavigator as screens and the entire TabNavigator wrapped in a Dumb component. I have edited my question sincerely.

Though this.props.navigation exists both inside the redux containers and the wrapper component, the navigation stack reference was different for them which is why methods such as navigate or goBack() were not working.

The solution was simple. Passing the navigation stack reference as screenProps to the wrapper component solves this issue.

<View style={{ flex: 1 }}>
  <AppBarComponent />
  <Tab screenProps={{ rootNav: this.props.navigation }} />
</View>

Upvotes: 1

bennygenel
bennygenel

Reputation: 24660

Solution for Method 1

Have a StackNavigator with only one screen and show your TabNavigator in that screen. Then customize header with your custom AppBarComponent.

header

React Element or a function that given HeaderProps returns a React Element, to display as a header. Setting to null hides header.

Sample

export default StackNavigator({
  stack: { 
    screen: Tab,
    navigationOptions: {
      header: (HeaderProps) => (<AppBarComponent headerProps={HeaderProps} />)
    }
  }
});

Solution for Method 2

You can wrap your component which is not a part of the stack with withNavigation HOC.

withNavigation is a higher order component which passes the navigation prop into a wrapped Component. It's useful when you cannot pass the navigation prop into the component directly, or don't want to pass it in case of a deeply nested child.

Sample

class AppBarComponent extends Component {
  static navigationOptions = { header: null }
  render() {
    return (
      <View>
        *some more views, buttons blah blah here
      </View>
    )
  }
}
export default withNavigation(AppBarComponent);

Upvotes: 1

Related Questions