Apparently I published this article with terribly bad timing as it came out right before React Navigation released their v5.x and most of this article is moot now. BUT FRET NOT! I CAN HELP!
Until I’ve found the time to rewrite this article, here’s a link to a newer one I’ve written on managing user navigation with the React Navigation v5.x:
Building games with React Native:
>> Setting yourself up for repeated success
If you are building a user-centric application, you will probably need a log in area and most likely the user won’t be able to get far without logging in as a registered user. Furthermore, it would be pretty bad UX if the user could accidentally log out by pressing his back button too often (at least on android).
One possible solution for this is based on react-navigation and navigation stacks. We will create separate navigation stacks for the login area and the actual app and manage the routes based on the user login state. For this, we’re going to use react-navigation as well as an additional package called react-navigation-stacks.
As we are going to concentrate on the navigation and user state part in this tutorial, I created a few pages beforehand which we’re going to use. As usual, you can grab my expo-playground at this tag to get started. If you’d prefer to use your own setup, feel free to download my scenes/ folder, which you can use with some minor tweaks and modifications.
Necessary changes if you use my scene/ folder without my repo:
Replace my Panel components with default react-native View components, add some simple styles for the layout and make sure your redux store is set up so you can use it for the tutorial. Other than that you should be good to go.
Here’s a list of the things I’m using for this tutorial:
- react / react-native
- redux / react-redux
- react-navigation / react-navigation-stacks
- react-native-elements (because I wanted some quick and easy to use UI elements)
For this tutorial, I’m going to assume that you have at least some prior knowledge about react native and redux. If that’s not the case, here’s a simple redux 101 I’ve written a while ago and I’ve got another tutorial about getting started with react-native and Expo.
When you start a new project with Expo, there’s usually an app.js as your starting point while with “regular” setups, you’ll probably find an index.js in your app’s root and those are the respective entry points for us.
The idea of react-navigation is to provide a wrapper that enriches its content (allowing access to the current navigation “state” and navigational functionality) while by itself not rendering any content except for optional navigation UI elements like a top or bottom navigation bar, tab bars, menus, sidebars etc.
Our first step will be to create a new navigation wrapper under src/routing and to import all scenes/pages that we’re going to use.
Then will create two navigation stacks, one for our login area and one for the actual app. I’ve named both stacks accordingly and added an initial route to both as an individual starting point. I’ve also added the headerMode: ‘none property to prevent the default navigation header to take up some space at the top of our application.
The third step is to create the navigation switch that allows us to move between both stacks. As our initial route here, we’re going to use the AppLoadingScreen, where we’re going to make a few checks and route the user according to their current login state.
And finally, we add our new Navigation Component to our app.jsx to check if everything is working as intended.
If you start the app in its current state, you’ll end up on our AppLoadingScreen and clicking/touching it won’t do anything so let’s change that.
To test our navigation, let’s add the navigation prop to our page. React navigation also serves as a “navigation object provider”. This means that inside our Navigation component, we can access a prop called navigation, kindly provided by our wrapper. We’ll add that to our prop types and use navigation.navigate(App) to trigger an actual navigation event.
Clicking the screen will now send us to the App stack’s initial route which we’ve previously defined as SceneGameHome.
GitHub: Added user actions and reducer
Right now we don’t handle the user login state at all so our next course of action will be to add the user to our state. For this tutorial, we’re going to mock the actual user login but we still need the respective login and logout actions.
In the user.reducer.js we’re going to set the initial state of user.isLoggedIn to false and handle two types of actions, userLogin and userLogout.
After registering the new reducer with our rootReducer.js we’re done here for now.
As we’ve set our initial state in our reducer, we can now map this state to our AppLoadingScreen component and let the clickHandler route the user according to his login state.
I would prefer to do this in the redux middleware as a side effect to the user’s click action but the current version of react-navigation is 4.x at the time of writing this article and only with the next (currently experimental) implementation there will be better integration with redux. So for the sake of not installing another package or passing the navigation object through the redux store, we’ll just decide our navigation route in the component for now.
What’s left now is actually handling the user login functionality besides storing the data in our Redux store. If a user successfully logs in, he’ll be sent to the AppStack after adding his name to our store.
We’re connecting our SceneLogin and map our state and dispatch functions to the props of our component.
Again, with better redux integration, I would handle both actions in the middleware, change the store in the reducer and have the middleware trigger the navigation actions.
To check whether our action reaches the reducer, we’re going to temporarily print the stored value in our template. That way we can check if the correct value reaches the store, currently “???” as we don’t grab the user’s name yet.
To grab the user name, we will make use of the useState hook and an onChange handler for the text input.
As we’re managing the navigation after the login via redux, we will give the user some visual feedback for the “successful fake login” and trigger a navigation event with a timer. This way the user will have to wait a moment but the end result still feels smooth as the user gets direct feedback upon clicking login. Also, since I don’t want to spend too much time on this, should the user not type in a name, we’ll just call him “anon” for now.
A short 3-second timer after his input should not feel too jarring for the user and that’s also enough to read the login success message.
As we’re using useEffect here, this hook will be called once on load and then again every time one of the properties in the array change. This means that when the user signs in and his name and login state change, the hook will generate a new message including his stored name.
As the user can only reach the actual game pages after logging in, we can now add his username to the page to make it feel more welcoming and personal.
GitHub: added BottomNavBar for app pages
To navigate between the different game pages of our app, we will create a small custom BottomNavBar ourselves. Had we used a different type of Navigation in our AppNavigator instead of createStackNavigator, we could now use the default bottom bar or tab navigation. Those Navigators are very convenient for your average “run of the mill” app with a streamlined design but as soon as you want to go a bit deeper, handle navigation events with more control or anything else not “off the shelf”, I prefer to build that myself.
We’re going to use the Button from react-native-elements but you could use any other button or one of your own button components instead. Also, I’m going to add small icons to each button using icons from FontAwesome which I’m grabbing from react-native-vector-icons.
Our BottomNavBar will be a simple but absolutely positioned View with a Button for each page.
Don’t forget to add the BottomNavBar to all 3 pages (I’ll skip 2 of them here for brevity). Make sure that they are in a container that doesn’t scroll to keep them pinned to the bottom of the page. If your page wrapper, in my case DefaultPage, is scrollable, make sure to have another container outside, pin the BottomNav to that element and include some bottom padding so that the bottom bar doesn’t overlap the last part of the page content.
GitHub: Added TopUserBar for Logout
Along with the bottom bar, let’s add a custom top bar for the logout button and user name.
Alternatively you could create the page wrapper with flex layout, use a top and bottom area and only make the part between scrollable. This decision depends on whether or not you want your content to scroll behint the top and bottom bars.
We can make utilise the useEffect hook to trigger the logout action once the user navigates to the logout page. As we only hand over [logout] to the hook and this value won’t change, useEffect will trigger only once on load.
And with that, we are done for now. The user can now log in and out, is navigating between two navigation stacks for this authentication flow and can navigate within both stacks.
Some words about me:
If you want to see more of my work and progress, feel free to check out my series on building a React Native Web App with Hooks, Bells, and Whistles with Expo SDK33. I’m also currently working on a new series about building more complex and scalable apps using React/Redux, where I’ll go into details about how and why I do stuff the way I do as well as some articles on my experience in building games for web and mobile with React.
And if you feel really supportive right now, you can always support me on patreon, thus allowing me to continue to write tutorials and offer support in the comments section.