Make sure you've already completed the setup guide, installation & have deployed your app before continuing.
TLDR
Add a userExistsInDb state variable.
Add onAuthStateChanged prop to DoormanProvider. This function gets called whenever the user signs in or out.
Inside this function, call your DB to see if user exists already, and if not, send to the onboarding flow.
Call setUserExistsInDb to update the state based on the DB's response.
When userExistsInDb is false, render an Onboarding screen that lets users enter information, and then adds them to the database. This goes inside of AuthGate.
Call setUserExistsInDb(true) when a user is successfully added to the DB from the Onboarding screen to update the state.
File overview
We're going to create 3 files. AuthLogic and Onboarding contain code distinct to this tutorial.
App.tsx initialize Firebase, render app
AuthLogic.tsx initialize Doorman, and conditionally render an authentication flow, an onboarding screen, or an authenticated app.
Onboarding.tsx Collect user information if they aren't in the DB yet.
1. Initialize Firebase in App
App.tsx
import AuthLogic from'./AuthLogic'import firebase from'firebase/app'import'firebase/auth'import'firebase/firestore'// <- if you use Firestoreif (!firebase.apps.length) {// you can replace this with your firebase configfirebase.initializeApp({ apiKey:'AIzaSyCn8HyP1tVZiagk-YvZRwjSwKdwQw5Pvng', authDomain:'tester-9d8bb.firebaseapp.com', databaseURL:'https://tester-9d8bb.firebaseio.com', projectId:'tester-9d8bb', storageBucket:'tester-9d8bb.appspot.com', messagingSenderId:'760778283392', appId:'1:760778283392:web:05cb35d0837c93c6584965', })}exportdefault AuthLogic
This is simple boilerplate code that is no different than normal initialization for Firebase/Doorman.
You can replace the firebase.initializeApp config variable with your own firebase config.
2. Initialize Doorman in AuthLogic
This tutorial will rely on the DoormanProvider implementation instead of withPhoneAuth. (They both achieve the same thing, as mentioned in the quick example.)
AuthGate accepts a single child: a function that returns a component. It receives the user and loading fields as arguments.
<AuthGate> {({ user, loading }) => {// if loading auth listenerif (loading) return <></>// if user is not authenticatedif (!user) return <AuthFlow />// your actual app component, which appears after// the user has authenticatedreturn <AfterAuth /> }}</AuthGate>
Whoa, where did AuthFlow come from? That's the Doorman component that lets your users sign in with phone number. AfterAuth is your normal app component.
Our file now looks like this:
AuthLogic.tsx (or .js if you don't use TypeScript)
import React from'react'import { DoormanProvider, AuthGate, AuthFlow } from'react-native-doorman'constAuthLogic= () => {return ( <DoormanProviderpublicProjectId="djzlPQFxxzJikNQgLwxN"> <AuthGate> {({ user, loading }) => {// if loading auth listenerif (loading) return <></>// if user is not authenticatedif (!user) return <AuthFlow />// your actual app component, which appears after// the user has authenticatedreturn <AfterAuth /> }} </AuthGate> </DoormanProvider> )}exportdefault AuthLogic
2.2 Add user exists in DB logic
Now, on to the code that lets you add your user to the DB.
First, let's add a userExistsInDb state variable, whose initial value is false. Add a checkingIfUserExists state variable too, which indicates if the DB check is loading or not.
Next, add a listener callback to the DoormanProvider component using its onAuthStateChanged prop.
constAuthLogic= () => {const [userExistsInDb,setUserExistsInDb] =useState(false)const [checkingIfUserExists,setCheckingIfUserExists] =useState(false)constonAuthStateChanged=async (user) => {if (user) {const { uid } = usersetCheckingIfUserExists(true)// user your own function here that calls your database to checkconstexists=awaitcheckIfUserExistsInDb(uid)// if you're using Firestore, it might look like this:const { exists } =awaitdb.doc(`users/${uid}`).get()// update the state based on our DB valuesetUserExistsInDb(exists)setCheckingIfUserExists(false) } else {// user is not signed in, reset the state to falsesetUserExists(false)setCheckingIfUserExists(false) } }return ( <DoormanProviderpublicProjectId="djzlPQFxxzJikNQgLwxN"onAuthStateChanged={onAuthStateChanged} > ... </DoormanProvider> )}
The onAuthStateChanged prop is a function that gets called whenever the user signs in or out. It follows the same API from firebase.
2.3 Render Onboarding
The final step is to render an Onboarding screen if userExistsInDb is false.
// 👇 We'll create this in the final step // you can import your own, too!import Onboarding from'./Onboarding'...<AuthGate> {({ user, loading }) => {// add checkingIfUserExists to loading conditionif (loading || checkingIfUserExists) return <></>if (!user) return <AuthFlow />// If user is authenticated, but doesn't exist in the DB,// render the onboarding flow.// We pass it a callback function called onUserAddedToDb// this function will set userExistsInDb to true// it gets called after the DB has been updated by <Onboarding />if (!userExistsInDb) {return ( <OnboardingonUserAddedToDb={() =>setUserExistsInDb(true)} /> ) }// your actual app component, which appears after// the user has authenticatedreturn <AfterAuth /> }}</AuthGate>
🎉That's it! All that's left is to make the Onboarding screen.
Here is our final AuthLogic screen, in its entirety:
import React, { useState } from'react'import { DoormanProvider, AuthGate, AuthFlow } from'react-native-doorman'import Onboarding from'./Onboarding'import AfterAuth from'./AfterAuth'import { db } from'./db'constAuthLogic= () => {const [userExistsInDb,setUserExistsInDb] =useState(false)const [checkingIfUserExists,setCheckingIfUserExists] =useState(false)return ( <DoormanProvideronAuthStateChanged={async user => {if (user) {const { uid } = usersetCheckingIfUserExists(true)// 👇 user your own function here that calls your database to check// const exists = await checkIfUserExistsInDb(uid)// if you're using Firestore, it might look like this:const { exists } =awaitdb.doc(`users/${uid}`).get()// update the state based on our DB valuesetUserExistsInDb(exists)setCheckingIfUserExists(false) } else {// user is not signed in, reset the state to falsesetUserExistsInDb(false)setCheckingIfUserExists(false) } }}publicProjectId="djzlPQFxxzJikNQgLwxN" > <AuthGate> {({ user, loading }) => {// add checkingIfUserExists to loading conditionif (loading || checkingIfUserExists) return <></>if (!user) return <AuthFlow />// If user is authenticated, but doesn't exist in the DB,// render the onboarding flow.// We pass it a callback function called onUserAddedToDb// this function will set userExistsInDb to true// it gets called after the DB has been updated by <Onboarding />if (!userExistsInDb) {return ( <OnboardingonUserAddedToDb={() =>setUserExistsInDb(true)} /> ) }// your actual app component, which appears after// the user has authenticatedreturn <AfterAuth /> }} </AuthGate> </DoormanProvider> )}exportdefault AuthLogic
Note: If you have your own Onboarding screen (or React Navigation stack, you can render it in place of Onboarding.
If you're using a React Navigation Stack in place of Onboarding (assuming it's with v5), then pass the onUserAddedToDb function as a param to your stack, and call it from your screen using useRoute().params.onUserAddedToDb.
3. Create <Onboarding /> screen
👋Don't forget to call the onUserAddedToDb function after successfully adding your user to the database, as seen in line 19 below! If you don't call it, the app won't re-render.
Here's an example of a basic Onboarding screen:
Onboarding.tsx
import React, { useState } from'react'import { TextInput, Button, View } from'react-native'import { useDoormanUser } from'react-native-doorman'constOnboarding= ({ onUserAddedToDb }) => {const [name,setName] =useState('')const { uid } =useDoormanUser()consthandleSubmit=async () => {// Implement your own DB function hereawaitaddUserToDb({ uid, name })// ...if you're using Firestore, it might look like:awaitdb.doc(`users/${uid}`).set({ name }, { merge:true })// Finally, once the user has been added, // call the callback function from the props.// 🚨if you forget to call this function, the app won't re-renderonUserAddedToDb() }return ( <Viewstyle={styles.container}> <TextInputplaceholder="Enter your name"value={name}onChangeText={setName} /> <Buttontitle="Submit name"onPress={handleSubmit} /> </View> )}exportdefault Onboardingconststyles=StyleSheet.create({ container: { flex:1, justifyContent:'center' }})
4. (Bonus) Create <AfterAuth />
Here's an example app screen, which you can make in your AfterAuth.tsx file:
import React from'react'import { Page, ScreenBackground, useDoormanUser, H1, Paragraph,} from'react-native-doorman'import { Button } from'react-native'constAfterAuth= () => {const { uid,signOut } =useDoormanUser()return ( <Pagestyle={{ marginTop:100, alignItems:'center' }}background={() => <ScreenBackground />} > <H1style={{ color:'white' }}>Welcome to Doorman.</H1> <Paragraphstyle={{ color:'white' }}> Sign out below, if {`you'd`} like. Your user id is {uid}. </Paragraph> <Buttontitle="Sign Out"color="white"onPress={signOut} /> </Page> )}exportdefault AfterAuth
5. If you're using Firestore
If you're using Firestore as your DB, you'll find examples in this tutorial for how to read and write a user.
For the sake of the tutorial, you can create a db.ts file, and populate it with this. You can also set a custom firebaseConfig from your own Firebase project.