Expo Router brings the best routing concepts from the web to native iOS and Android apps. Every file in the app directory automatically becomes a route in your mobile navigation, making it easier than ever to build, maintain, and scale your project. expo.github.io/router/docs
Expo File Based Routing ( Part 1 )
The first part of working with Expo Router and File Based Navigation is covered in the video below
Firebase Authentication ( Part 2 )
The second part has the steps listed below with a video walking through the whole project in GitHub. Source code for both videos in included in the GitHub link at the end of the post.
Setup
npx expo install firebase
npx expo install @react-native-async-storage/async-storage
The rest is based on the directions provide by Expo Firebase Integration Documentation
Create the firebase configuration file
Needed to add persistence to get React Native to remember the previous logged in user; this is why we added the react-native async-storage library.
// firebase-config.js
const firebaseConfig = { ... }
export const app = initializeApp(firebaseConfig);
export const auth = initializeAuth(app, {
persistence : getReactNativePersistence(AsyncStorage)
})
We now export the auth object and use that throughout the application instead of calling getAuth
in.
We want the app to remember logged in users so we need to add additional code. To check for an authenticated user we need to call onAuthStateChange
when app launches and wait for a response before rendering the proper Navigation Stack.
In our store, we have added the user
and the initialized
properties to the application state. After the onAuthStateChange
is triggered we update the store if there is a user, and we indicate that the application is now initialized. We can access the updated values through the store use those values in index.js
// store.js
export const AuthStore = new Store({
isLoggedIn: false,
initialized: false,
user: null,
});
const unsub = onAuthStateChanged(auth, (user) => {
console.log("onAuthStateChange", user);
AuthStore.update((store) => {
store.user = user;
store.isLoggedIn = user ? true : false;
store.initialized = true;
});
});
In index.js we wait for initialization to complete and then we take the appropriate action to redirect the user to the login route if there is no user or the tabs route if a user exists.
const { initialized, isLoggedIn } = AuthStore.useState();
React.useEffect(() => {
if (!navigationState?.key || !initialized) return;
const inAuthGroup = segments[0] === "(auth)";
if (
// If the user is not signed in and the initial segment is not anything
// segment is not anything in the auth group.
!isLoggedIn &&
!inAuthGroup
) {
// Redirect to the login page.
router.replace("/login");
} else if (isLoggedIn) {
// go to tabs root.
router.replace("/(tabs)/home");
}
}, [segments, navigationState?.key, initialized]);
We wrap the firebase calls so we can update the store when appropriate. Below is the full listing of the calls for login, logout and create user account.
export const appSignIn = async (email, password) => {
try {
const resp = await signInWithEmailAndPassword(auth, email, password);
AuthStore.update((store) => {
store.user = resp.user;
store.isLoggedIn = resp.user ? true : false;
});
return { user: auth.currentUser };
} catch (e) {
return { error: e };
}
};
export const appSignOut = async () => {
try {
await signOut(auth);
AuthStore.update((store) => {
store.user = null;
store.isLoggedIn = false;
});
return { user: null };
} catch (e) {
return { error: e };
}
};
export const appSignUp = async (email, password, displayName) => {
try {
// this will trigger onAuthStateChange to update the store..
const resp = await createUserWithEmailAndPassword(auth, email, password);
// add the displayName
await updateProfile(resp.user, { displayName });
AuthStore.update((store) => {
store.user = auth.currentUser;
store.isLoggedIn = true;
});
return { user: auth.currentUser };
} catch (e) {
return { error: e };
}
};
Video ( Part 2 )
Related Links
- Using Firebase w React Native - docs.expo.dev/guides/using-firebase
- Async Storage - docs.expo.dev/versions/latest/sdk/async-sto..
- Auth persistence Issue Firebase SDK - github.com/firebase/firebase-js-sdk/issues/..
- PullState - lostpebble.github.io/pullstate
Source Code
Firebase integration is in a separate branch in the repo