I have added an update to this article in the end, Do check it out
The React-Native community is finally settling for React-Navigation as the default navigation solution. React-Navigation has a vast and powerful API and one of the best thing is that React-Navigation provides really great support for redux, but the bad news which I personally felt is a lack of proper documentation. In this article I try to explain the basics of integrating React-Navigation with Redux and finally introducing authentication flow to the app. I have divided this writing in two parts, Part-1 contains the basic setup for integrating redux & react-navigation, Part-2 contains the logical flow for authentication. Both the parts are contained in this article.
Prerequisites
To continue reading you should have experience with at least React-Native, React-Navigation, Redux. I havenât used any authentication API(that isnât a issue in the demo, the flow will be same as a real world app) in the demonstration app. In Real world situation you will need something like redux-thunk etc. I have used Redux-Persist to mimic the real-world behaviour of persisting user session.
PART-1: Setting up React-Navigation with redux
The whole idea of integrating React-Navigation with redux is to manage navigation state in redux. Thus we can manage all of the app state from the global redux store easily.
In Part-1 we will be integrating redux and React-navigation by creating a navigation reducer which will handle the state for the navigation and add the reduxâs dispatch method to handle actionâs dispatched through redux. I have created small demonstrable gists for better step by step understanding, the whole project code for understanding how these little parts integrate in the current PART-1 is available here (repoâs master branch):
shubhnik/redux-react-navigation_redux-react-navigation - React-Native + Redux + Redux-Persist + React Navigation_github.com
First letâs get familiar with some react-navigationâs API, we will use these APIs while integrating redux:
[addNavigationHelpers](https://reactnavigation.org/docs/navigators/custom#addNavigationHelpers)
: This API helps us to pass our ownnavigation
prop to the Navigator. Thenavigation
prop passed to the navigator will override the defaultnavigation
prop.[getActionForPathAndParams](https://reactnavigation.org/docs/routers/api#getActionForPathAndParams-path-params)(path, params)
: This API receivespath(the key defined for a route in Navigator
andparams
and returns an action which is needed to update the navigation state. In simple words it tells us âhey! You need to perform this action to navigate to this pathâ. Consider the following example, see the console.log below, it indicates to perform aNAVIGATE
action to therouteName: âscreen1â
:
3. [getStateForAction(action, state)](https://reactnavigation.org/docs/routers/api#getStateForAction-action-state)
: We have calculated the action for navigating to a particular route using getActionForPathAndParams
, now we need to update the state of the Navigator to actually navigate to the route. For this, getStateForAction
comes handy. It receives the action and the current state of the navigator and returns the new updated state. In simple words it says hey! I am giving you a new navigation state based on the action and the current state you provided to me. It will return null if it couldnât understand the action provided. Consider this sample demo code just to understand how getStateForAction
works:
We have calculated the new state, now we want to pass this state to the Navigator. So lets get the act of integrating redux and react-navigation done:
- Letâs create a reducer navigationReducer which will handle the navigation state:
The above reducer returns the new state for the navigator. newState
will be null
if an action provided to getStateForAction
isnât understandable, that case could be when we are trying to navigate to a route which is not defined in our navigator AppNavigator
.
- Now lets pass the calculated new state from the reducer to the navigator(AppNavigator). We will also pass the dispatch to the navigator. For this purpose we will be using
addNavigationHelpers
API as discussed above. Following is the basic code we will use to pass the newnavigation
prop (containingstate
anddispatch
) to the navigator(AppNavigator):
Here in line 17 in the above code snippet, we have passed the dispatch
and state
to the navigator. All the routes defined in AppNavigator
will receive these state
and dispatch
.
- Now what? How do we navigate to the new route(screen)? What we have to do is dispatch an action and the
[basicNavigationReducer](https://gist.github.com/shubhnik/b55602633aaeb5919f6f3c15552d1802)
will calculate the new state and pass it to the navigator. The navigation state will be updated and we will navigate to the new route. Suppose we are currently on screen1 , following is a demonstration of how to dispatch an action to navigate to screen2:
[NavigationActions](https://reactnavigation.org/docs/navigators/navigation-actions)
API of react-navigation returns an action object as shown in the gist above. The returned object is same as that returned by getActionForPathAndParams
.
Now we have checked all the parts: a reducer to handle navigation state, passing our own navigation prop to the navigator, dispatching actions to update navigation state. We saw them in gists, now you should check all those parts assembled and working together, understand the flow in here:
shubhnik/redux-react-navigation_redux-react-navigation - React-Native + Redux + Redux-Persist + React Navigation_github.com
PART-2: Implementing authentication flow
In this part we will be creating an authentication flow using redux and react-navigation. We will be following this navigator for reference:
const AppNavigator = StackNavigator({login: {screen: Login},screen1: {screen: Screen1},screen2: {screen: Logout}});
Here is how our authentication flow will work:
- If a user isnât signed in, he/she will be shown the
login
screen, he/she canât accessscreen1
orscreen2
. - A signed in user cant go back to the login screen without logging out.
- If a user signed in, kills the app and then reopens the app,
screen1
will be presented, not thelogin
screen.
We will be using this demonstrable minimal loginReducer to manage our login states:
The most important logic of managing state for authentication flow is managed by our navigation reducer. Our [basicNavigationReducer](https://gist.github.com/shubhnik/b55602633aaeb5919f6f3c15552d1802)
has transformed into a little more capable reducer which can handle authentication flow, letâs take a look at our new navigationReducer:
Letâs get through this navigationReducer
step by step:
-
In line 4, we are calculating
ActionForLoggedOut
, the action we need to navigate tologin
screen when the user isnât logged in. And in line 7, we are calculatingstateForLoggedOut
which will return the navigation state for the logged out user i.e login screen will be displayed. -
In line 5, we are calculating
ActionForLoggedIn
, the action we need to navigate toscreen1
when the user is logged in. And in line 8, we are calculatingstateForLoggedIn
which will return the navigation state for the logged in user i.e screen1 will be displayed. -
In line 9, we are assembling
stateForLoggedOut
andstateForLoggedIn
intoinitialState
. Based on if the user is logged in we will passstateForLoggedIn
or if not logged in than we will passstateForLoggedOut
frominitialState
to our navigator,AppNavigator
: -
In line 19 of
[navigationReducer.js](https://gist.github.com/shubhnik/9f7bafd3145e66434705cd1e995d1356)
, consider theLogin
case. On dispatchingLOGIN
action theisLoggedIn
field is set totrue
in[loginReducer.js](https://gist.github.com/shubhnik/55cb0a9f82d6eeb20004e43ee6b3e746)
, and correspondingly we will update thestateForLoggedIn
, now our navigation stack will consist of two routes:screen1
stacked overlogin
screen. Thus we will navigate toscreen1
. We will configure thescreen
by disabling gestures and the back button, because we want to keep thelogin
route in our navigation stack for the logout case. -
In line 25 of
[navigationReducer.js](https://gist.github.com/shubhnik/9f7bafd3145e66434705cd1e995d1356)
, consider theLogout
case. On dispatchingLOGOUT
action theisLoggedIn
field is set to false which results instateForLoggedOut
being fed to the navigator. Thus we have to update thestateForLoggedOut
chunk of the navigation state and reset it to contain only thelogin
route. -
In line 36 of
[navigationReducer.js](https://gist.github.com/shubhnik/9f7bafd3145e66434705cd1e995d1356)
, all the dispatched actions exceptLOGIN
andLOGOUT
are handled in default case. Currently in the demonstration app the only action that is being handled here is to navigate toscreen2
. Sincescreen2
is only accessible by logged in users, hence we need to update thestateForLoggedIn
only. -
One more pending case is handling the action
@@redux/INIT
. This action is automatically dispatched by redux when the app is opened. We are considering this action for this case: Suppose we hadnât consider this action, than, If the user is already logged in, thescreen1
will be displayed to the user. The current navigation stack will only containscreen1
. Now user navigates toscreen2
and dispatches theLOGOUT
action from there. Since forLOGOUT
actionlogin
screen needs to be displayed but it isnât in our current navigation stack. Thus a not so smooth animation will happen(see the header animation of left gif below). But since we are handling the@@redux/INIT
and we are inserting thelogin
route at the bottom of the navigation stack (see line 16), hence we are enabling a smooth transition animation for logging out(right gif below). Consider the following two animations(without and with handling@@redux/INIT
):
On left we arenât handling the â@@redux/INITâ and hence the unpleasant header animation, on right we are handling the â@@redux/INITâ action and hence a more natural and a pleasant header animation.
By now we have investigated the navigationReducer
which is handling the authentication flow. The authentication flow has been set up now. I have used Redux-Persist for persisting user session. React-Navigation and redux have finally found harmony with authentication flow đ.
For checking the complete authentication flow and how it plays along, checkout the authFlow branch of this repo for better understanding:
shubhnik/redux-react-navigation_redux-react-navigation - React-Native + Redux + Redux-Persist + React Navigation_github.com
Handling the hardware back button in Android
Since we are handling the navigation state with redux, thus react-navigation loses its control over managing state. Thus we have to handle back button in Android on our own. The official docs of react-navigation has a great documentation for handling back button in Android here:
Redux Integration Guide | React Navigation_Edit description_reactnavigation.org
Whatâs next?
The most important part of integrating redux with react-navigation is done. Adding more complex nested navigation wonât be any tough task now. I will be adding more demos for nested navigations to the github repo . Donât forget to star and watch the repo for more updates. You can also fork the repo and share your solutions with the community.
If this guide happens to be useful for you, than do show some love with claps and donât forget to share it with others đ đ. You can follow me on twitter where sometimes my tweets have some meaning.
UPDATE
- Initially I have used
[**@@redux/INIT**](https://github.com/shubhnik/redux-react-navigation/blob/authFlow/src/Reducers/navigationReducer.js#L25)
which isnât a kind of good practice. We shouldnât deprive redux of its own actions đ˘. You can check how I am managing state without using@@redux/Init
in the nestedNavigators branch.
2. Since I published this article, I have added two more branches nestedTabs and nestedNavigators demonstrating more complex navigation structure.
3. Discussions/feedbacks are appreciated đ.