ReactNative Expo File Based Routing with Firebase Authentication
Tags: expo, reactnative, firebase, tutorial
Published: 2023-04-26 22:08:02

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

{% youtube WNZbARN7lMM %}


###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**](https://docs.expo.dev/guides/using-firebase/)

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](https://docs.expo.dev/versions/latest/sdk/async-storage/).

```javascript
// 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`
```javascript
// 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.

```javascript
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.
```javascript

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 )
{% youtube Os5_DRhN2Aw %}

### Related Links
- Using Firebase w React Native - https://docs.expo.dev/guides/using-firebase/
- Async Storage - https://docs.expo.dev/versions/latest/sdk/async-storage/
- Auth persistence Issue Firebase SDK - https://github.com/firebase/firebase-js-sdk/issues/6050
- PullState - https://lostpebble.github.io/pullstate/

###Source Code

> Firebase integration is in a separate branch in the repo

{% github aaronksaunders/reactnative-file-router-1 %}