DEV Community

Cover image for ReactNative Expo File Based Routing with Firebase Authentication
Aaron K Saunders
Aaron K Saunders

Posted on

ReactNative Expo File Based Routing with Firebase Authentication

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. https://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


Enter fullscreen mode Exit fullscreen mode

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)
})


Enter fullscreen mode Exit fullscreen mode

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;
  });

});


Enter fullscreen mode Exit fullscreen mode

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]);


Enter fullscreen mode Exit fullscreen mode

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 };
  }
};


Enter fullscreen mode Exit fullscreen mode

Video ( Part 2 )

Related Links

Source Code

Firebase integration is in a separate branch in the repo

GitHub logo aaronksaunders / reactnative-file-router-1

React Native Code Walkthrough: Expo Router Tabs With Authentication App






Top comments (2)

Collapse
 
perroudsky profile image
perroudsky

hey man, thanks for this !
Did you forget to call onAuthStateChanged ? I can't see it anywhere, and without it I guess the persistence would not work ?

Collapse
 
aaronksaunders profile image
Aaron K Saunders

I check for a user when I launch the app, I donโ€™t need to call onAuthStateChanged. If there is a user navigate to private page, if not navigate to login