A comprehensive guide for integrating React-Navigation with Redux including authentication flow

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):

First let’s get familiar with some react-navigation’s API, we will use these APIs while integrating redux:

  1. addNavigationHelpers : This API helps us to pass our own navigation prop to the Navigator. The navigation prop passed to the navigator will override the default navigation prop.
  2. getActionForPathAndParams(path, params) : This API receives path(the key defined for a route in Navigator and params 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 a NAVIGATE action to the routeName: ‘screen1’:

3. 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:

*NewStateOfNavigator* containing the new route “screen2” and index for new navigation state updated to 2.

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 new navigation prop (containing state and dispatch ) 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 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 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:

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 access screen1 or screen2.
  • 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 the login 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 has transformed into a little more capable reducer which can handle authentication flow, let’s take a look at our new navigationReducer:

navigationReducer.js

Let’s get through this navigationReducer step by step:

  • In line 4, we are calculating ActionForLoggedOut, the action we need to navigate to login screen when the user isn’t logged in. And in line 7, we are calculating stateForLoggedOut 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 to screen1 when the user is logged in. And in line 8, we are calculating stateForLoggedIn which will return the navigation state for the logged in user i.e screen1 will be displayed.
  • In line 9, we are assembling stateForLoggedOut and stateForLoggedIn into initialState. Based on if the user is logged in we will pass stateForLoggedIn or if not logged in than we will pass stateForLoggedOut from initialState to our navigator,AppNavigator:
conditionally passing state to navigator based on if user is logged in or not
  • In line 19 of navigationReducer.js, consider the Login case. On dispatching LOGIN action the isLoggedIn field is set to true in loginReducer.js, and correspondingly we will update the stateForLoggedIn, now our navigation stack will consist of two routes: screen1 stacked over login screen. Thus we will navigate to screen1. We will configure the screen by disabling gestures and the back button, because we want to keep the login route in our navigation stack for the logout case.
  • In line 25 of navigationReducer.js, consider the Logout case. On dispatching LOGOUT action the isLoggedIn field is set to false which results in stateForLoggedOut being fed to the navigator. Thus we have to update the stateForLoggedOut chunk of the navigation state and reset it to contain only the login route.
  • In line 36 of navigationReducer.js , all the dispatched actions except LOGIN and LOGOUT are handled in default case. Currently in the demonstration app the only action that is being handled here is to navigate to screen2. Since screen2 is only accessible by logged in users, hence we need to update the stateForLoggedIn 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, the screen1 will be displayed to the user. The current navigation stack will only contain screen1. Now user navigates to screen2 and dispatches the LOGOUT action from there. Since for LOGOUT action login 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 the login 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:

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:

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

  1. Initially I have used @@redux/INIT 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 😄.

Topics of interest

More Related Stories