Shovon · Follow
Published in ·
--
EDIT: When this article was written, react-navigation was version 1. There have been many changes in it when it reached version 3. Some parts of the article have been updated accordingly with adding few extra details.
In this article, you will learn various ways to navigate in your application using React Navigation. I will assume that you know the basics of React Navigation. I will show you how to properly set, access and pass properties in different screens to make navigation easy. Make sure you have read the official docs before starting this to get accustomed with this library.
React Navigation is not the only contender for navigating in a mobile application, you can also find other navigation libraries, namely React Native Router Flux, React Native Navigation and Native Navigation.
The last two plugin are “native” navigation i.e. they are built on top of the Android and iOS platform navigational components, unlike React Navigation which is a JS based solution.
If you are confused whether to use a JS based solution or a native one, maybe this post can help you (brace for a long read!).
You can implement React Navigation both with Redux and without Redux. Since we are starting from scratch, we will start with “without Redux”.
We will go with building a news application. I will name it FuNA (Futuristic News Application).
Before we begin, here’s what you need to know about React Navigation —
For navigation you need a Navigator and there are three different types of Navigator in it — StackNavigator, TabNavigator and DrawerNavigator.
As of v3, there is a new navigator named SwitchNavigator.
Whenever you register a component with any of these Navigators, then only navigation
prop is passed to the registered component. That prop is responsible for all navigation inside the application.
I will work with blank screens and just focus on the solutions of the following requirements —
- Navigating to a screen
- Navigating to nested Child screen from Parent screen
- Navigating to nested Child screen from a different Parent screen
- Navigating to nested Child screen from a Child screen of different Parent
- Accessing navigation in a Component
- Resetting screen to first Parent screen, from a nested screen
Hers’s a prototype of the app —
The screens will be arranged like this —
AppStackNav
|- HomeScreen
|- LoginScreen
|- SignupScreen
|- AppDrawerNav
|- NewsStackNav
| |- NewsTabNav
| | |- NewsGlobalScreen
| | |- NewsLocalScreen
| |- NewsDetailScreen
|- AccountTabNav
|- AccountProfileScreen
|- AccountSettingScreen
And, here’s what the final app will look like—
We will use all our examples from this repository(for react-navigation v1). Let’s code.
Navigating to a screen —
If you have a simple StackNavigator
like below,
// navigators/AppStackNav.jsconst AppStackNav = StackNavigator({
Home: {
screen: Home,
navigationOptions: {
header: null
}
},
Login: {
screen: Login,
navigationOptions: {
header: null
}
}
});
Then to navigate to Login
from Home
, you would simple call
this.props.navigation.navigate("Login")
Or
this.props.navigation.dispatch(
NavigationActions.navigate({ routeName: "Login" })
);
Similarly for,
- Navigating to nested Child screen from Parent screen
- Navigating to nested Child screen from a different Parent screen
- Navigating to nested Child screen from a Child screen of different Parent
just pass the required routeName
in navigate
function.
this.props.navigation.navigate("routeNameOfScreenToGo")
In v1, for a successful screen change, the routeName had to be accessible in the screen from where you were calling the navigate function i.e. we had to make custom actions if we had to go to any specific nested/cousin screens.
In v3, navigate function itself determines how to navigate to passed routeName.
Resetting to a screen —
Now suppose, once someone successfully logs in, you would like to prevent them to go back to login/signup screen. Here’s how that would be possible
// screens/Login.jsthis.props.navigation.dispatch(
NavigationActions.reset({
index: 0,
actions: [NavigationActions.navigate({ routeName: "Dashboard" })]
})
);
Here reset
ting with index: 0
would make the screen passed in routeName
as the initial screen, removing all previous screens from the stack.
As of v3, due to introduction of SwitchNavigator we can change our navigation setup to following —
AppSwitchNav
|- UnauthStackNav
|- HomeScreen
|- LoginScreen
|- SignupScreen
|- AppDrawerNav
|- NewsStackNav
| |- NewsTabNav
| | |- NewsGlobalScreen
| | |- NewsLocalScreen
| |- NewsDetailScreen
|- AccountTabNav
|- AccountProfileScreen
|- AccountSettingScreen
SwitchNavigator resets routes to their default state when you switch away from UnauthStackNav
to AppDrawerNav
.
Accessing navigation in a Component —
If you use a Component to render a navigator, then you have to keep in mind to pass that navigator’s router
to Component’s router
and pass the navigation
prop to the Component navigation
(since that Component is not registered as any navigator, it will not receive any navigation
prop automatically).
// screens/Dashboard.jsimport React, { Component } from "react";
import AppDrawerNav from "../navigators/AppDrawerNav";export default class Dashboard extends Component {
static router = AppDrawerNav.router;render() {
return <AppDrawerNav navigation={this.props.navigation} />;
}
}
Passing ans accessing data from among screens —
To pass any data in the destination screen, you can pass your data in params
.
// screens/NewsLocal.jsthis.props.navigation.navigate("NewsDetail", { topic: "React Navigation" })
To pass data in the current screen, you can call setParams
—
this.props.navigation.setParams({
key1: 'value1',
key2: 'value2',
})
To access data passed in params
in the destination screen, you can access it in props.navigation.state.params
.
You can also call getParam
—
this.props.navigation.getParam('KeyToFind', 'OptionalDataToShowIfKeyNotFound')
In our case, accessing props.navigation.state.params.topic
would give React Navigation
in the destination screen.
Going back from a screen to previous screen —
this.props.navigation.goBack()
Resetting screen to first Parent screen, from a nested screen —
Let’s consider a situation when someone logs out of the application. In that case you would want them to go to Home
(assumed) without being able to go back. Here’s how you can do that.
// screens/Dashboard.jsthis.props.navigation.dispatch(
NavigationActions.reset({
index: 0,
key: null,
actions: [NavigationActions.navigate({ routeName: "Home" })]
})
)
Notice the key: null
that has been added for this case. This generally should not be required but when you are in a child screen and want to reset to parent screen, navigator cannot identify other routes than the routes accessible to current screen.
This means if you are in NewsGlobal
and you click logout icon (in our app), then the only available routes the navigator finds is NewsTabNav
and NewsDetail
. As of now there is no solution for this, except this workaround key: null
.
You can read more about this issue here.
As of v3,
reset
is only available inStackActions
, which means you can only call this function from aStackNavigator
.
Going back more than one screen —
Suppose if you traversed screen in this fashion ScreenA
→ ScreenB
→ ScreenC
, then if you want to goBack
from ScreenC
to ScreenA
, you would have to pass thekey
of that screen as params in goBack
this.props.navigation.goback(’keyOfTheVisitedScreenToGo’)
You can manually setkey
when going to a screen. So whenever you want to go back from that screen, you have to pass that key.
You can read more about this here.
As of v3 there are few other changes that you should know about (which might feel unexpected).
Navigating back in navigator —
To navigate in a navigator if you use navigate
, then going back will always take you to initialRoute
of that navigator. You can prevent that in drawer
and tab
by providing backBehavior: ‘none'
but that gives rise to different cases too.
If you have the following setup —
App
|- Stack1
|- Screen1
|- Screen2
|- Screen3
|- Stack2
|- Screen4
|- Screen5
|- Screen6
And you are currently on Screen3
. Now when you call navigate('Screen6')
you will go to Screen6
. From here if you go back normally, you will first go to Screen4
then to Screen3
.
If you need navigating back from Screen6
to Screen3
you need to call dispatch
function with custom action
.
this.props.navigation.dispatch(
NavigationActions.navigate({
routeName: 'Stack2',
action: NavigationActions.navigate({
routeName: 'Screen6',
})
})
)
This works because when you pass action
it prioritises child action than the top level action. See details in this PR.
For this case you can also manually go back via props.navigation.goBack(null)
or props.navigation.pop()
.
You should also keep in mind similar case does not work for DrawerNavigator
.
I have made a Snack Expo where you can find various cases where simply calling props.navigation.goBack()
or pressing hardware back button (without adding event handler) would not take you back as you expect them to.
Learning React Navigation is easy. Initial stages are easier too. But as an app grows more complex it becomes necessary to make a right choice in the structure of the navigation of the app as you would not be able to make it work with different type of navigator(like whether to register a component inside a
stack
or atab
or adrawer
).
Before going into React Navigation you should keep in mind that, it is still new and may contain bugs and side effects. It can happen that you got stuck in one problem for long hours. So before going into any navigation plugin do your research thoroughly.
That’s all for now. Hope you find this useful. If you think this can help someone else too, pass it on.
Peace!