Chat app with React Native (part 2) - Firebase Email Authentication with react-native-firebase
In the first part of this tutorial series to build a chat-based app in React Native, we learned how to create reusable form elements using the react-native-paper UI library. Along with that, we learned how to install the navigation library react-navigation and configure a basic authentication stack navigator using two routes.
In this tutorial, let us start using a backend service to add real-time features to the Chat app. For backend services, I am going to use Firebase. You are going to learn how to install and configure Firebase SDK in a react native app with the help of react-native-firebase
module as well as set up and configure Email authentication. In order to follow this tutorial and future posts, you are required to use a Firebase project.
Create a new Firebase project from console
🔗To access the Firebase credentials for each mobile OS platform and configure them to use Firebase SDK, create a new Firebase project or use one if you have access already from Firebase console, you can skip this step.
Create a new project as shown below.
Complete the details of your Firebase project:
Click the button Create project and you are going to be redirected to the dashboard screen. That's it. You have successfully created a new Firebase project.
Now make sure that the Email Sign-in method is enabled. From the Firebase console and navigate to Authentication section from the side menu.
Go to the second tab Sign-in method and make sure to enable the Email sign-in provider.
Add Firebase SDK to React Native app
🔗If you have used react-native-firebase
version 5 or below, you must have noticed that it was a monorepo that used to manage all Firebase dependencies from one module.
Version 6 of this library wants you to only install those dependencies based on Firebase features that you want to use. For example, in the current app, to support the email authentication feature you are going to install the auth and core app package.
From the terminal window execute the following command.
yarn add @react-native-firebase/app @react-native-firebase/auth
Add Firebase credentials to your iOS app
🔗Firebase provides a file called GoogleService-Info.plist
that contains all the API keys as well as other credentials for iOS devices to authenticate the correct Firebase project.
To get these credentials, go to back to the Firebase console in a browser window. From the dashboard screen of your Firebase project, open Project settings from the side menu.
Go to Your apps section and click on the icon iOS to select the platform.
Enter the application details and click on Register app.
Then download the GoogleService-Info.plist
file as shown below.
Open Xcode, then open the file /ios/ChatApp.xcodeproj
file. Right-click on the project name and Add Files option, then select the file to add to this project.
Then open ios/ChatApp/AppDelegate.m
and add the following header.
1#import <Firebase.h>
In the same file, within the didFinishLaunchingWithOptions
method, add the following configure method.
1- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {2 if ([FIRApp defaultApp] == nil) {3 [FIRApp configure];4 }
Lastly, go back to the terminal window to install pods.
1cd ios/ && pod install2# after pods are installed3cd ..
Make sure you build the iOS app.
1npx react-native run-ios
That's it. The configuration to set up a Firebase SDK and credentials in a React Native app is complete.
Create a home screen
🔗In the previous post, you have successfully configured an Auth stack that displays those screens when the end-user is not authorized or logged in inside the app. There are a set of screens that are only going to be accessible to the user when they are logged in. Let us call the group of screens that are visible after login, home stack.
One such screen is going to be a home screen where all the chat rooms are going to be listed. In this section, let us start by creating a basic home screen such that you can complete navigational flow between the home stack and the auth stack.
Create a new screen component called HomeScreen.js
inside src/screens/
directory with the following code snippet.
1import React from 'react';2import { View, StyleSheet } from 'react-native';3import { Title } from 'react-native-paper';45function HomeScreen() {6 return (7 <View style={styles.container}>8 <Title>Home Screen</Title>9 <Title>All chat rooms will be listed here</Title>10 <FormButton modeValue="contained" title="Logout" />11 </View>12 );13}1415export default HomeScreen1617const styles = StyleSheet.create({18 container: {19 backgroundColor: '#f5f5f5',20 flex: 1,21 justifyContent: 'center',22 alignItems: 'center'23 }24});
Create home stack navigator
🔗Create a new stack navigator file called HomeStack.js
inside src/navigation.js
that is going to have those routes which are only available after logging in. You can think of these routes as protected routes.
Open this file and add the following code snippet. Nothing new is going in terms of creating a stack navigator as shown below.
1import React from 'react';2import { createStackNavigator } from '@react-navigation/stack';3import HomeScreen from '../screens/HomeScreen';45const Stack = createStackNavigator();67export default function HomeStack() {8 return (9 <Stack.Navigator>10 <Stack.Screen name="Home" component={HomeScreen} />11 </Stack.Navigator>12 );13}
Create an auth provider
🔗In this section, you are going to create an authentication provider to check whether the user is logged in or not and access them if they are logged in.
Create a new file called AuthProvider.js
inside src/navigation/
. Start by importing the following statements.
1import React, { createContext, useState } from 'react';2import auth from '@react-native-firebase/auth';
Then create an AuthContext
and make sure to export it since you are going to use it on several different screens.
1export const AuthContext = createContext({});
In Reactjs, the Context API is designed to share data that is considered global for a tree of React components. When you are creating a context (like above), there is a requirement to pass a default value. This value is used when a component does not have a matching Provider.
The Provider allows the React components to subscribe to the context changes. To create an auth provider, export a function called AuthProvider
. This provider is going to allow the screen components to access the current user in the application. Define a state variable called user
.
1export const AuthProvider = ({ children }) => {2 const [user, setUser] = useState(null);34 return (5 <AuthContext.Provider6 value={{7 user,8 setUser,9 login: async (email, password) => {10 try {11 await auth().signInWithEmailAndPassword(email, password);12 } catch (e) {13 console.log(e);14 }15 },16 register: async (email, password) => {17 try {18 await auth().createUserWithEmailAndPassword(email, password);19 } catch (e) {20 console.log(e);21 }22 },23 logout: async () => {24 try {25 await auth().signOut();26 } catch (e) {27 console.error(e);28 }29 }30 }}31 >32 {children}33 </AuthContext.Provider>34 );35};
In the value
prop above, also define some functions. These functions are now available to be used anywhere in the screens component tree using React Context.
Each of the functions is consuming Firebase methods to interact with real-time Firebase backend service. Both the login and register functions require the user's email
and password
to verify/save credentials. The logout method invokes a simple signOut()
method. All these Firebase methods are available from the @react-native-firebase/auth
package. Do note that, all these functions are asynchronous actions and thus, using async await
syntax helps.
Wrapping routes with auth provider
🔗Now, that the provider is created, but how to use for a set of components in the current app tree? Well, you have to wrap this provider around the Routes
such as to use the helper functions as well as the value of current user
(as described above) in the screen components.
Open navigation/index.js
file and modify it as follows.
1import React from 'react';2import { Provider as PaperProvider } from 'react-native-paper';3import { AuthProvider } from './AuthProvider';4import Routes from './Routes';56/**7 * Wrap all providers here8 */910export default function Providers() {11 return (12 <PaperProvider>13 <AuthProvider>14 <Routes />15 </AuthProvider>16 </PaperProvider>17 );18}
Remember, from the previous post, we added that comment that to wrap all components using all providers in this file? Well, that's what this file is for.
Check if the user is logged in or not
🔗To check if the user is logged or not, let us modify the navigation/Routes.js
file. Using the value of the user
from the auth provider, you are going to switch between the stack navigators. To start, make sure you imported the following statements.
1import React, { useContext, useState, useEffect } from 'react';2import { NavigationContainer } from '@react-navigation/native';3import auth from '@react-native-firebase/auth';4import AuthStack from './AuthStack';5import HomeStack from './HomeStack';6import { AuthContext } from './AuthProvider';7import Loading from '../components/Loading';
From the above snippet, ignore the Loading
component for now. You are going to create it at the end of this section.
Now, inside the Routes
function, you are two define two state variables initializing
and loading
to check whether the user's state is logged in or not. Also, from the context value, fetch user
and setUser
.
Then, define a function called onAuthStateChanged
which is going to handle user state changes. Using useEffect
hook, you can subscribe to this state change function and make sure you unsubscribe it when the component unmounts. This method allows you to subscribe to real-time events when the user performs an action. The action here can be, logging in, signing out, and so on.
1export default function Routes() {2 const { user, setUser } = useContext(AuthContext);3 const [loading, setLoading] = useState(true);4 const [initializing, setInitializing] = useState(true);56 // Handle user state changes7 function onAuthStateChanged(user) {8 setUser(user);9 if (initializing) setInitializing(false);10 setLoading(false);11 }1213 useEffect(() => {14 const subscriber = auth().onAuthStateChanged(onAuthStateChanged);15 return subscriber; // unsubscribe on unmount16 }, []);1718 if (loading) {19 return <Loading />;20 }2122 return (23 <NavigationContainer>24 {user ? <HomeStack /> : <AuthStack />}25 </NavigationContainer>26 );27}
Lastly, create a new component file called Loading.js
inside src/components/
directory. This component is going to be responsible to display a loading spinner.
1import React from 'react';2import { View, ActivityIndicator, StyleSheet } from 'react-native';34export default function Loading() {5 return (6 <View style={styles.loadingContainer}>7 <ActivityIndicator size="large" color="#6646ee" />8 </View>9 );10}1112const styles = StyleSheet.create({13 loadingContainer: {14 flex: 1,15 alignItems: 'center',16 justifyContent: 'center'17 }18});
Completing the app
🔗In order for the user to perform auth actions in the app, you have to use the context in each of the screen components for different actions.
Start by opening LoginScreen.js
. Import useContext
from react and AuthContext
from AuthProvider
.
1import React, { useState, useContext } from 'react';2// rest of the import statements remain same3import { AuthContext } from '../navigation/AuthProvider';45export default function LoginScreen({ navigation }) {6 const { login } = useContext(AuthContext);78 // rest remains statements9}
Inside the LoginScreen
function, make sure to add an onPress
prop as shown below.
1<FormButton2 title="Login"3 modeValue="contained"4 labelStyle={styles.loginButtonLabel}5 onPress={() => login(email, password)}6/>
Similarly, you have to modify the SignupScreen.js
file.
1import React, { useState, useContext } from 'react';2// rest of the import statements remain same3import { AuthContext } from '../navigation/AuthProvider';45export default function SignupScreen({ navigation }) {6 const { register } = useContext(AuthContext);7 // rest remains statements8}910// Add the onPress prop to <FormButton />1112<FormButton13 title="Signup"14 modeValue="contained"15 labelStyle={styles.loginButtonLabel}16 onPress={() => register(email, password)}17/>;
Lastly, modify the HomeScreen
to add a sign out button and when the user is in the logged-in state, display their user uid
(the unique identifier in Firebase to differentiate and store different users).
1import React, { useContext } from 'react';2import { View, StyleSheet } from 'react-native';3import { Title } from 'react-native-paper';4import { AuthContext } from '../navigation/AuthProvider';5import FormButton from '../components/FormButton';67function HomeScreen() {8 const { user, logout } = useContext(AuthContext);910 return (11 <View style={styles.container}>12 <Title>Home Screen</Title>13 <Title>All chat rooms will be listed here</Title>14 <Title>{user.uid}</Title>15 <FormButton16 modeValue="contained"17 title="Logout"18 onPress={() => logout()}19 />20 </View>21 );22}23export default HomeScreen2425const styles = StyleSheet.create({26 container: {27 backgroundColor: '#f5f5f5',28 flex: 1,29 justifyContent: 'center',30 alignItems: 'center'31 }32});
Go to the simulator, and you are going to get similar results as shown below. Perform these steps. Try creating a new user from the sign-up screen, and you are going to get their uid
on the home screen.
You can verify the uid
of the current user by going to the dashboard screen from Firebase console.
Conclusion
🔗Congratulations! You've completed this tutorial and successfully added an authentication flow between the two stack navigators. In the next part of this series, we'll explore more features such as creating and storing chat rooms in a collection in Firestore, as well as displaying all chat rooms on the home screen. To create a new chat room, we'll create a new modal screen and make changes to the current home stack accordingly.
What's Next?
🔗In the next post of this series, we are going to explore how to create a modal screen using react-navigation
stack navigator. This modal screen is going to have separate navigator as well as to be used to create a new chat room.
Then, we are going to add Firebase NoSQL database Firestore and add a query to store the name of a chat room in a collection.
You can find the complete source code for this project at this Github repo.
👉 Here is a list of resources used in this tutorial:
- Reactjs Context API
- Firebase Authentication reference from
react-native-firebase
- Getting started with stack navigator using
react-navigation
v5 here
Originally published at Heartbeat.Fritz.Ai.
More Posts
Browse all posts