How to Integrate Firebase with Expo React Native
Tweet
I worked on an app dev project recently that demanded fast turn around and cross-platform compatibility, so using React Native with the Expo managed workflow was the most efficient choice. However, as soon as I needed to integrate Firebase with Expo React Native, a problem came up. The most popular library, React Native Firebase, is not compatible with the Expo managed workflow. What to do?
Fortunately, the react-firebase-hooks library comes to rescue. If you are already using hooks in your React development, the benefits are clear:
- you can access and manage states in a function, which means you no longer need to write classes for your components;
- you can control when the side effect functions are called with useEffect;
- you can write custom hooks that are reusable in different functional components. react-firebase-hooks provide hooks that act as wrappers and listeners for Firebase methods. This tutorial will walk you through how to manage Firebase Auth's auth state using react-firebase-hooks.
Create a Firebase Project
Add a project, choose your settings, register your app, and what we need here is the firebaseConfig information. We are going to copy and paste this information into our project.
const firebaseConfig = {
apiKey: "[APIKEY]",
authDomain: "[AUTHDOMAIN]",
databaseURL: "[DATABASEURL]",
projectId: "[PROJECTID]",
storageBucket: "[STORAGEBUCKET]",
messagingSenderId: "[MESSAGINGSENDERID]",
appId: "[APPID]",
measurementId: "[MEASUREMENTID]",
};
Add Firebase Configuration to Your Project Install the Firebase SDK. I am using npm:
$ npm install --save firebase
Create a config
folder at the root of your Expo React native project.
Inside the config
folder, create a file firebase.tsx
(or firebase.js
if you are not using Typescript). You will store your Firebase configuration information here.
import * as firebase from "firebase";
// Config info unique to your project
const firebaseConfig = {
apiKey: "[APIKEY]",
authDomain: "[AUTHDOMAIN]",
databaseURL: "[DATABASEURL]",
projectId: "[PROJECTID]",
storageBucket: "[STORAGEBUCKET]",
messagingSenderId: "[MESSAGINGSENDERID]",
appId: "[APPID]",
measurementId: "[MEASUREMENTID]",
};
// Initialize Firebase
const Firebase = firebase.initializeApp(firebaseConfig);
// Export the Firebase App so the rest of the app can have access to it
export default Firebase;
Using the Auth Hook
Install the react-firebase-hooks library to your project:
$ npm install --save react-firebase-hooks
In your App.tsx
(or App.js
), import the useAuthState
hook and the already initialized Firebase project from the config folder:
import { useAuthState } from "react-firebase-hooks/auth";
import Firebase from "./config/firebase";
import React from "react";
export default function App() {
// useAuthState hook takes a Firebase auth instance to monitor
const [user, loading, error] = useAuthState(Firebase.auth());
return (
<SafeAreaProvider>
<Navigation />
<StatusBar />
</SafeAreaProvider>
);
}
Now you can access the current user object in user! If the user is logged in, user returns firebase.User
, or undefined
if not.
Note: Refer to the react-firebase-hooks auth documentation for specific usage and examples.
Making the User Object Globally Available Using React Context
In the step above, the reason we initialized the useAuthState
hook in App.tsx
is that it is the top-level component. The next step is to pass the user object down to the child screens. We will use React Context, designed to "share data that can be considered 'global' for a tree of React components."
First, we create a User Context. Create a folder context
in the root directory of our react native project. Then, create the file UserContext.tsx
in the context
folder.
import React from "react";
import * as firebase from "firebase";
export default (React.createContext < undefined) | (firebase.User > undefined);
Our App.tsx
will be the UserContext
Provider, and the value we want to pass into the provider is the user object initialized by Firebase. In our App.tsx
, we will wrap the return view in a UserContext.Provider
component, and pass user
as the value to the provider:
import { useAuthState } from "react-firebase-hooks/auth";
import Firebase from "./config/firebase";
import React from "react";
export default function App() {
// useAuthState hook takes a Firebase auth instance to monitor
const [user, loading, error] = useAuthState(Firebase.auth());
return (
<UserContext.Provider value={user}>
<SafeAreaProvider>
<Navigation />
<StatusBar />
</SafeAreaProvider>
<UserContext.Provider>
);
}
The child components (AKA the screens) are consumers of the UserContext
. To access user information in a child component, import the UserContext
hook, and then call useContext(UserContext);
.
The code below is a bare bone log in screen
:
import { useContext } from "react";
import UserContext from "../context/UserContext";
import React from "react";
export default function LogInScreen() {
// Now you have access to the user object!
const user = useContext(UserContext);
// Hooks for tracking email and password changes
const [email, onChangeEmail] = React.useState("");
const [password, onChangePassword] = React.useState("");
function handleSignIn(
email: string,
password: string,
user: undefined | firebase.User
) {
Firebase.auth()
.signInWithEmailAndPassword(email, password)
.then(() => {
alert("logged in!");
})
.catch((error) => alert(error));
}
return (
<SafeAreaView>
<TextInput value={email} onChangeText={(email) => onChangeEmail(email)}></TextInput>
<TextInput value={password} onChangeText={(password) => onChangePassword(password)}></TextInput>
<TouchableHighlight onPress={() => handleSignIn(email, password, user)}><TouchableHighlight>
</SafeAreaView>
);
}
You can now log in a user.
Conclusion
Congrats for finishing the tutorial!
- We learned about using React Hooks and why they are superior;
- We used React Context to manage global information;
- Now we can incorporate Firebase into our Expo React Native project!