SERIES: React Native (Step by Step) — Auth Flow Navigation with Mock User Login
By: Konrad Abe
Tags: React Navigation, Redux, Expo, Typescript, React Native

Preparations

For our AuthFlow example, we need a few additional screens. For one we want to allow the user to both sign in (existing users) and sign up (new users) and in addition to the AppLoading screen (for loading / initialising user data) I’ve added an AppCheck screen where I will, later on, check for version updates in the app stores and such.

I went one step further and split them into AuthStack (sign in/sign up) and InitStack (splash screen, and a screen for the app to check version updates and such). We won’t use this immediately but that way we won’t have to reshuffle everything later on.

Here is a list of our old and new screen components, sorted by their stack. You can copy one of the existing screens, I copied the old SplashScreen as it already had the redux code we’ll use later. For the AppCheckScreen I simply made a copy of our AppLoadingScreen from before.

Don’t worry too much about this, we will address those later, one by one.

In our MainNavigation component, we add the new screens accordingly. The stacks separation I prepared above won’t be represented here for now so Splash, AppCheck, SignIn and SignUp are all for the “not logged in” users.

Skippable Splash Screen

Starting with the SplashScreen, we’re building a small mechanism to trigger a navigation event after a few seconds or when the user taps the screen to trigger it manually. This is a common pattern you see in many apps.

Instead of the useEffect hook you are probably familiar with by now, we will be using the useFocusEffect() hook from react navigation because it will “mount” and “unmount” when the screen gains and loses focus. A common pitfall of Navigation stacks is that screens do not “unmount” when you navigate away within the same stack which means that your useEffect() cleanup will not be triggered.

On focus of this screen, we start a timeout and store the timeoutID in a const to use for the clean-up of the running timer when the user taps on the screen to navigate. As recommended by the docs, we are wrapping our code in a useCallback() hook to prevent running the code too often and do the same for the navigate() function that we use for timer and onPress event.

I had to bump my eslint for hooks from 4.0.0 to 4.0.3 to get rid of a false positive for useFocusEffect as the “Effect” suffix triggered a rule it should not. If you use my code from the last session, you might need to do that too. Version bumped to this => “eslint-plugin-react-hooks”: “^4.0.3”

At this point and to test our new pages and routes, our SignUp and SignIn screens will be mocked placeholders, simply using the redux action to mock a successful login. I’ve also added a small button to navigate between SignUp and SignIn.

Mocking the login

I will briefly go over the mock login I’m using. To represent real server/api interaction, I’ve mocked a promise using a timeout that will return the expected mock values. I’m skipping the types I’ve used and advise you to simply copy the full file from GitHub as this is only a mock authentification and not the focus of this article.

We can now use this to create a simple login and register call. The fake login can be set to fail by supplying an additional boolean as a third parameter (useful for testing) but other than that will expect an email and password and tries to match those to our user array. All in plain text, as you would expect from a first class cheap mock api.

I’ve added a slight variation in form of a createAccount function as well for the SignUp. The response contains a token that would be saved and used in all following requests to pass as a logged-in user in a real application.

Wiring up Redux and Mock API

We now need to do the real work, setting up the real redux code for users so our previous user.ts duck will now be rewritten completely.

Let’s start with our annotations and initial state. The login will be a simple boolean, just as before but additionally we will keep track of the login state to make a differentiation between not logged in, logged out and failed to log in as well as loading during the login.

Also, we will store the user email, a message text and a flag to mark newly registered users. This way we can display login errors from our api or a message for a user that successfully logged out and perform additional things for new users that might not have been fully set up yet, depending on the app you are planning to build.

Actions

We will use the setLogin action to store a logged-in user’s info in our state. This action will no longer be called directly from the app. Instead, we will use if in the userMiddleware we are about to write.

The new setLogout action will contain the new loginState and message, again mainly to be used within our middleware.

When the user tries to log in or sign up, he will only enter an email and password. We will do the rest in our middleware and reducer.

Selectors

For easier access, we will set up a total of 3 selectors for different parts of our state to be used in the frontend. We know selectLogin already. Additionally, we now have a flag for isSubmitting during the api request and the loginMessage, to update the UI accordingly and provide useful feedback for user interactions.

Middleware

In our middleware, we will listen to login and signup attempts, call our mock code and dispatch additional actions according to the results from the mock api.

As you see, we simply dispatch the setLogout action to pass on the new loginState and error message.

Reducer

The last bit is our Reducer code. This is where it all comes together. When the user attempts to log in, we update state and message to reflect this in the UI and then wait for updates via setLogin and setLogout.

A proper login form

On our SignIn and SignUp screens, we can now replace the mock button with a propper login form from a separate component file.