React Navigation has become a standard in navigating between screens in the React Native. There are just four basic navigators, with an option to create a custom one, but the magic happens when you combine them in the right way.
Stack navigator
- transitions between screens
- navigation history on stack
- built-in header
Bottom tab navigator
- no navigation history
- buggy API for returning active tab
- highly customizable bottom tab
Switch navigator
- no additional UI
- automatic screen unmount on leave
- returns to initial screen on back button press (no navigation history)
We’ll skip the Drawer Navigator
to use the Tab Navigator
Instead.
First navigator
SwitchNavigator
will be the type of the root navigator. Add Splash
screen which automatically redirects to the Launch
screen and also write SignUpName
and SignUpVerifyEmail
with simple buttons that change screens.
const PrimaryNav = createSwitchNavigator(
{
Splash: SplashScreen,
Launch: LaunchScreen,
SignUpName: SignUpNameScreen,
SignUpVerifyEmail: SignUpVerifyEmailScreen
},
{
initialRouteName: 'Splash',
backBehavior: 'initialRoute'
}
)
export default PrimaryNav
Tab navigation
Inside SwitchNavigator
, include TabNav
with its 3 screens. It’s important to do it directly here and not to render it like <TabNav />
to keep navigation state in one place and avoid denying access to the screens in incorrectly rendered navigation from the PrimaryNav
screens.
const TabNav = createBottomTabNavigator({
Home: HomeScreen,
Notifications: NotificationsScreen,
Info: InfoScreen
})
const PrimaryNav = createSwitchNavigator(
{
MainNav: TabNav,
Splash: SplashScreen,
Launch: LaunchScreen,
SignUpName: SignUpNameScreen,
SignUpVerifyEmail: SignUpVerifyEmailScreen
},
...
Nested stack navigator
Next, add separate Stack Navigator
s for each tab. Because it includes a header by default, and we already built one, create invisibleHeader
object to unpack it in the config.
const invisibleHeader = {
headerMode: 'none',
navigationOptions: {
headerVisible: false
}
}
Stack navigators:
const HomeScreenNav = createStackNavigator(
{
Home: HomeScreen
},
{
...invisibleHeader,
initialRouteName: 'Home'
}
)
const NotificationsScreenNav = createStackNavigator(
{
Notifications: NotificationsScreen,
Notification: NotificationScreen
},
{
...invisibleHeader,
initialRouteName: 'Notifications'
}
)
const InfoScreenNav = createStackNavigator(
{
Info: InfoScreen
},
{
...invisibleHeader,
initialRouteName: 'Info'
}
)
And modifiy the PrimaryNav
to replace screens with new navigators
const PrimaryNav = createSwitchNavigator(
{
MainNav: TabNav,
Splash: SplashScreen,
Launch: LaunchScreen,
SignUpName: SignUpNameScreen,
SignUpVerifyEmail: SignUpVerifyEmailScreen
},
...
Hide tab bar when going deep
Change bottom Tab config to hide the TabBar
when switching away from the initial screen of each Stack Navigator
.
for (let nav of [HomeScreenNav, NotificationsScreenNav, InfoScreenNav]) {
nav.navigationOptions = ({ navigation }) => ({
tabBarVisible: navigation.state.index <= 0
})
}
Common screens in stack
We want to have access to some common screens in multiple places, without compromising the history integrity. The idea is to always keep navigators in a straight tree to prevent switching up and down the tree between navigators (branches) when we need to access common screens (actually any given screen). To do so, we simply duplicate common screens in each Stack Navigator
. We keep it in a separate object:
const commonScreens = { Settings: SettingsScreen }
Next, add prefixes to each common screen, depending on the Stack Navigator
initial screen name. If you are lazy like me, you can use ready-made frunctions from Ramda and Ramda-adjunct (my go-to utils libraries, like lodash but fully functional and never mutating input):
import * as R from 'ramda'
import { renameKeysWith } from 'ramda-adjunct'
const generateCommonScreens = prefix =>
renameKeysWith(R.concat(prefix), commonScreens)
Or just write your own. It’s simply adding a prefix to each object key.
Then also unpack it to each Stack Navigator
like so:
const HomeScreenNav = createStackNavigator(
{
...generateCommonScreens('Home'),
Home: HomeScreen
},
...
After it navigate to whatever common screen we want and pass according screen name depending on what tab it has to navigate from, e.g. HomeSettings
and InfoSettings
.
Custom back button behavior
There is no reliable built-in way to get the current screen in TabNavigator
. To customize the back button behavior, create a custom container to wrap screens with.
import React, { Component } from 'react'
import { withNavigation } from 'react-navigation'
import { BackHandler } from 'react-native'
class HandleBack extends Component {
constructor(props) {
super(props)
const { navigation } = this.props
this.state = {
active: false
}
this.didFocus = navigation.addListener('didFocus', payload => {
this.setState({ active: true })
})
this.didBlur = navigation.addListener('didBlur', payload => {
this.setState({ active: false })
})
BackHandler.addEventListener('hardwareBackPress', this.onBack)
}
componentWillUnmount() {
this.didFocus.remove()
this.didBlur.remove()
BackHandler.removeEventListener('hardwareBackPress', this.onBack)
}
onBack = () => {
if (this.state.active) {
BackHandler.exitApp()
return true
}
return false
}
render() {
return this.props.children
}
}
export default withNavigation(HandleBack)
We have to manually save whether the current tab is active or not. Explicit cleanup of event listeners is also a good practice. Lastly, wrap the Home Screen
with this component in the render function.
Talk with our experts about custom software development now!
Q: What is React Navigation?
React Navigation is a library for navigating between screens in a React Native app. It has four basic navigators, with the option to create a custom one, and allows for combinations of navigators to create a customized navigation experience.
Q: What are the different types of navigators in React Navigation?
The four basic navigators in React Navigation are Stack Navigator, Tab Navigator, Switch Navigator, and Drawer Navigator.
Q: How can I create nested Stack Navigators?
To create nested Stack Navigators, include a separate Stack Navigator for each tab within the Tab Navigator. Make sure to set the header to ‘none’ and set the initialRouteName to the desired screen.