Build Refactor: A React app with the new Firebase v9.x Web SDK
Published: 2022-11-28 10:18:41

The release of version 9 of the [Firebase Web SDK](https://github.com/firebase/firebase-js-sdk) has introduced breaking changes in methods for managing users and querying databases. Code written in Firebase v8.x will throw errors when used in v9.x, which calls for refactoring.

In this article, we’ll learn how to refactor a React app that uses the Firebase Web SDK v8.x to v9.x., which is also called the [modular Web SDK](https://firebase.google.com/docs/web/modular-upgrade). For our example, we’ll use an Amazon clone built with v8.x and refactor it to v9.x. Let’s get started!

## Prerequisites
To follow along with this tutorial, you should be familiar with React and Firebase v8.x. You should also have Node.js installed on your machine.

## Introducing Firebase v9.x Web SDK
The new web SDK moves away from the namespace approach that was used in version 8. Instead, it adopts a modular format optimized for eliminating unused code, for example, tree shaking, resulting in a significant reduction in the JavaScript bundle size.

The transition to a modular approach has introduced breaking changes, making the new library backward incompatible and causing the code used in v8.x to throw errors in the new Firebase v9.x SDK.

The following code shows some of the breaking changes introduced in the new library:


```
// VERSION 8
import firebase from 'firebase/app';
import 'firebase/auth'; 

firebase.initializeApp();
const auth = firebase.auth();

auth.onAuthStateChanged(user => { 
  // Check for user status
});


// VERSION 9 EQUIVALENT
import { initializeApp } from 'firebase/app';
import { getAuth, onAuthStateChanged } from 'firebase/auth';

const firebaseApp = initializeApp();
const auth = getAuth(firebaseApp);

onAuthStateChanged(auth, user => {
  // Check for user status
});
```
Both code samples above monitor a user state. Although both are similar in the number of lines of code used, in v9.x, instead of importing the `firebase` namespace or the `firebase/auth` side effect, which augments authentication service to the `firebase` namespace, we are importing and using individual functions.

These changes take advantage of the code elimination features of modern JavaScript tools like [Webpack and Rollup](https://blog.logrocket.com/benchmarking-bundlers-2020-rollup-parcel-webpack/).

For example, the v8.x code above includes the following code snippet:

```
auth.onAuthStateChanged(user => { 
  // Check for user status
});

```
`auth` is a namespace and a service that contains the `onAuthStateChanged` method. The namespace also contains methods like `signInWithEmailAndPassword`, `createUserWithEmailAndPassword`, and `signOut`, which are not being used by the code. When we bundle our entire code, these unused methods will also be included in the bundle, resulting in a relative size increase.

Though bundlers like Webpack and Rollup can be used to eliminate unused code, due to the namespace approach, they will have no effect. Solving this issue is one of the primary goals of remodeling the API surface to take a modular shape. To learn more about the reasons behind the changes in the new library, check out the [official Firebase blog](https://firebase.googleblog.com/2021/08/deep-dive-into-the-new-firebase-js-sdk-design.html).

## Firebase compatibility library

The new SDK also includes a compatibility library with a familiar API surface, which is fully compatible with v8.x. The compatibility library allows us to use both old and new APIs in the same codebase, enabling us to progressively refactor our app without breaking it. We can use the compatibility library by making a few tweaks to the import paths as follows:

```
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
```
We’ll take advantage of the library when we refactor our Amazon clone app.

## Firebase Web SDK v9.x benefits

In short, the Firebase Web SDK v9.x offers reduced size and increased performance overall. By taking advantage of code elimination features through JavaScript tools like Webpack and Rollup, the new web SDK offers a faster web experience. With the new modular shape, the new SDK is said to be about 80 percent smaller than its predecessors, according to the official [Firebase Twitter](https://twitter.com/Firebase?ref_src=twsrc%5Egoogle%7Ctwcamp%5Eserp%7Ctwgr%5Eauthor) account.

## Firebase Tweet v9 80 Percent Size

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uo59wfqd0stmqp093y1k.png)

## Setting up our React app for refactoring

Now that we’re familiar with the new SDK, let’s learn how to refactor our v8.x app. The Amazon clone app that we’ll use in this section is an ecommerce app built using Firebase and Strapi.

In our app, we used Firebase to add features like managing user identity with Firebase authentication and storing products purchased by authenticated users with Cloud Firestore. We used Strapi to handle payments for products bought on the app. Finally, we created an API with Express.js that responds with the Strapi client secret of a customer who is about to purchase a product with Firebase Cloud Functions.

You can access a [deployed version of the site](https://challange-5f99e.web.app/), which looks like the image below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/858uvc5vcnzwa8gi1xje.png)

## Amazon Clone App

Feel free to play around with the app to better understand what we’re working on in this article.

## Setting up the Amazon clone app

Before we begin coding, first, let’s clone the repo from GitHub and install the necessary npm packages. Open your terminal and navigate to the folder you would like to store the React app in. Add the following commands:

```
$ git clone https://github.com/Tammibriggs/Amazon-clone-FirebaseV8.git
$ cd Amazon-clone-FirebaseV8
```
Now that we’ve successfully cloned the repo, we need to change the Firebase version in the `package.json` file to v9.x before we install the packages.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64fw5ubsua8ha1t4b3b3.png)

In the root directory, open the `package.json` file and replace `"firebase": "8.10.0"` in the dependencies object with `"firebase": "9.2.0"`. Now, let’s install our app’s dependencies by running the following command in the terminal:

```
$ npm install 
$ cd functions
$ npm install 
```
Although we’ve set up and installed all of our app’s dependencies, if we try running the app with `npm start`, it will throw errors. To avoid this, we need to fix our app’s breaking changes, which we’ll do shortly.

## React app structure

The structure for the `src` directory of our app is as follows, but we’ve removed all the style files to make it look shorter:

```
src
 ┣ Checkout
 ┃ ┣ Checkout.js
 ┃ ┣ CheckoutProduct.js
 ┃ ┗ Subtotal.js
 ┣ Header
 ┃ ┗ Header.js
 ┣ Home
 ┃ ┣ Home.js
 ┃ ┗ Product.js
 ┣ Login
 ┃ ┗ Login.js
 ┣ Orders
 ┃ ┣ Order.js
 ┃ ┗ Orders.js
 ┣ Payment
 ┃ ┣ axios.js
 ┃ ┗ Payment.js
 ┣ App.js
 ┣ firebase.js
 ┣ index.js
 ┣ reducer.js
 ┣ reportWebVitals.js
 ┗ StateProvider.js
```
We’ll only be working with the files that use Firebase services, `firebase`, `App.js`, `Header.js`, `Login.js`, `Payment.js`, and `Orders.js`,

## Refactoring the Amazon clone to a modular approach

Let’s update to the v9.x compat library, helping us to progressively migrate to a modular approach until we no longer have any need for the compat library.

The upgrade process follows a repeating pattern; first, it refactors code for a single service like authentication to the modular style, then removes the compat library for that service.

## Update imports to the v9.x compat library

Head to the `firebase.js` file in the `src` directory and modify the v8.x import to look like the following code:

```
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
```
With just a few alterations, we’ve updated the app to the v9.x compat. Now, we can start our app with `npm start`, and it won’t throw any errors. We should also start the Firebase function locally to expose the API that gets the client secret from Strapi.

In your terminal, change to the `functions` directory and run the following command to start the function:

` $ firebase emulators:start
`

## Refactoring authentication codes

In the `Login.js`, `App.js`, and `Header.js`, we used the Firebase authentication service. First, let’s refactor the code in the `Login.js` file, where we created the functionality to create a user and sign them in with the Firebase `createUserWithEmailAndPassword` and `signInWithEmailAndPassword` methods. When we scan through the `Login.js` file, we’ll see the following v8.x code:

```
// src/Login/Login.js
const signIn = e => {
    ...
    // signIn an existing user with email and password
    auth
      .signInWithEmailAndPassword(email, password)
      ....
  }

  const regiter = e => {
    ...
    // Create a new user with email and password using firebase
    auth
      .createUserWithEmailAndPassword(email, password)
      ....
  }  
```
To follow the modular approach, we’ll import the `signInWithEmailAndPassword` and `createUserWithEmailAndPassword` methods from the `auth` module, then update the code. The refactored version will look like the code below:

```
// src/Login/Login.js
import {signInWithEmailAndPassword, createUserWithEmailAndPassword} from 'firebase/auth'

...
const signIn = e => {
  ...
  // signIn an existing user with email and password
  signInWithEmailAndPassword(auth, email, password)
  ...
}
const regiter = e => {
  ...
  // Create a new user with email and password using firebase
  createUserWithEmailAndPassword(auth, email, password)
  ...
}  
```
Now, let’s refactor the `App.js` and `Header.js` files. In the `App.js` file, we used the `onAuthStateChanged` method to monitor changes in the user’s sign in state:

```
// src/App.js
useEffect(() => {
  auth.onAuthStateChanged(authUser => {
    ...
  })
}, [])
```
The modular v9.x of the code above looks like the following segment:

```
// src/App.js
import {onAuthStateChanged} from 'firebase/auth'

...
useEffect(() => {
  onAuthStateChanged(auth, authUser => {
    ...
  })
}, [])
```
In the `Header.js` file, we used the `signOut` method to sign out authenticated users:

```
// src/Header/Header.js
const handleAuthentication = () => {
  ...
     auth.signOut()
  ...
}
```
Update the code above to look like the code snippet below:

```
// src/Header/Header.js
import {signOut} from 'firebase/auth'
...
const handleAuthentication = () => {
  ...
    signOut(auth)
  ...
}
```
Now that we’re done refactoring all the authentication codes, it’s time to remove the compat library to gain our size benefit. In the `firebase.js` file, replace `import 'firebase/compat/auth'` and `const auth = firebaseApp.auth()` with the following code:

```
import {getAuth} from 'firebase/auth'
...
const auth = getAuth(firebaseApp)
```

## Refactoring Cloud Firestore codes

The process for refactoring Cloud Firestore code is similar to what we just did with the authentication codes. We’ll be working with the `Payment.js` and `Orders.js` files. In `Payment.js`, we use Firestore to store the data of users that paid for products on the site. Inside Payment.js, we’ll find the following v8.x code:

```
// src/Payment/Payment.js
...
db
  .collection('users')
  .doc(user?.uid)
  .collection('orders')
  .doc(paymentIntent.id)
  .set({
    basket: basket,
    amount: paymentIntent.amount,
    created: paymentIntent.created
  })
...
```
To refactor the code, we first have to import the necessary functions, then update the rest of the code. The v9.x of the code above looks like the following:

```
// src/Payment/Payment.js
import {doc, setDoc} from 'firebase/firestore'

...
const ref = doc(db, 'users', user?.uid, 'orders', paymentIntent.id)
setDoc(ref, {
  basket: basket,
  amount: paymentIntent.amount,
  created: paymentIntent.created
})
...
```
In the `Orders.js` file, we used the `onSnapshot` method to get real-time updates of the data in Firestore. the v9.x code looks like the following:

```
// src/Orders/Orders.js
....
db
  .collection('users')
  .doc(user?.uid)
  .collection('orders')
  .orderBy('created', 'desc')
  .onSnapshot(snapshot => {
     setOrders(snapshot.docs.map(doc => ({
       id: doc.id,
       data: doc.data()
     })))
  })
...
```
The v9.x equivalent is as follows:

```
import {query, collection, onSnapshot, orderBy} from 'firebase/firestore'

...
const orderedOrders = query(ref, orderBy('created', 'desc'))
onSnapshot(orderedOrders, snapshot => {
     setOrders(snapshot.docs.map(doc => ({
       id: doc.id,
       data: doc.data()
     })))
  })
...
```
Now that we’re done refactoring all the Cloud Firestore codes, let’s remove the compat library. In the `firebase.js` file, replace `import 'firebase/compat/firestore'` and `const db = firebaseApp.firestore()` with the following code:

```
import { getFirestore } from "firebase/firestore";
...
const db = getFirestore(firebaseApp)
...
```

## Update initialization code

The final step in upgrading our Amazon clone app to the new modular v9.x syntax is to update the initialization code. In the `firebase.js` file, replace `import firebase from 'firebase/compat/app'`; and const `firebaseApp = firebase.initializeApp(firebaseConfig)` with the following functions:

```
import { initializeApp } from "firebase/app"
...
const firebaseApp = initializeApp(firebaseConfig)
...
```
Now, we’ve successfully upgraded our app to follow the new v9.x modular format.

Conclusion
The new Firebase v9.x SDK provides a faster web experience than its v8.x predecessor, thanks to its modular format. This tutorial introduced the new SDK and explained how to use its compact library to reflector a React app. You should be able to follow the methods and steps outlined in this article to upgrade your own apps to the newest version.