Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Get Started

react-firebase-state is a component library that helps you manage server-side state from Firebase Auth and Firestore within a React web app. It also provides utilities for managing client-side state that you control.

Installation

To use react-firebase-state, you must install four peer dependencies if you don’t already have them:

  • react
  • react-dom
  • immer
  • firebase

In your react project folder, use npm to install these dependencies:

npm install react react-dom immer firebase

Then install the react-firebase-state component library.

npm install @gmcfall/react-firebase-state

Prerequisites

This documentation assumes that you are familiar with React and Firebase. Most of the examples assume some knowledge of Typescript, but you can use the library with plain-old Javascript.

Using the library

The react-firebase-state library provides a collection of hooks and other utilities for managing state. To use the library in the child components of your application, you must wrap them within a <FirebaseProvider> component.

Wrap your application

import { FirebaseProvider } from '@gmcfall/react-firebase-state';

// You must initialize the FirebaseApp instance that your application uses
// and pass it to the <FirebaseProvider> component.

// It is not necessary to implement a function called "initializeFirebaseApp".  
// You could initialize Firebase in a module and simply export `firebaseApp` 
// as a constant. How you perform the initialization is up to you.

const firebaseApp = initializeFirebaseApp();

export function App() {

    return (
        <FirebaseProvider firebaseApp={firebaseApp}>
           { /* Add your child components here*/ }
        </FirebaseProvider>
    )
}

Usage in child components

There are lots of ways to use the react-firebase-state library. Here, we merely illustrate two common use cases:

For other use cases, explore the list of available guides.

Access the current user

This example shows how you can get information about the currently authenticated user.

import { useAuthListener } from '@gmcfall/react-firebase-state';

export function ComponentThatAccessesTheCurrentUser() {
    
    const [user, userError, userStatus] = useAuthListener();

    switch (userStatus) {
        case "pending":
            // Information about the current user is being fetched
            // asynchronously. You might want to render a spinner.
            // `user` and `userError` are undefined. 
            break;

        case "signedIn":
            // The current user is signed in. 
            // The `user` variable contains the Firebase `User` object.
            // `userError` is undefined.
            break;        

        case "signedOut":
            // The current user is signed in. 
            // The `user` variable holds the  Firebase `User` object.
            // `userError` is undefined
            break;

        case "error":
            // An error occurred while fetching information about the user.
            // `user` is undefined.
            // `userError` contains the Error thrown by Firebase
            break;
    }

    // ...
}

Listen for changes to a Firestore document

In this example, a component listens for changes to a Firestore document containing information about a city. The id for the city document is passed via props.

import { useEffect } from "react";
import { useDocListener } from '@gmcfall/react-firebase-state';

export function SomeComponent({ cityId }) {

    const [city, cityError, cityStatus] = useDocListener("SomeComponent", ["cities", cityId]);

    switch (cityStatus) {

        case "idle":
            // The path passed to `useDocListener` contains an undefined
            // value, and therefore a document listener was not started
            // `city` and `cityError` are both undefined.
            break;

        case "pending":
            // The document is being fetched asynchronously and
            // the response is pending.
            // The `city` and `cityError` variables are undefined.
            break;

        case "success":
            // The document was successfully retrieved from Firestore.
            // The `city` variable contains the document data.
            // `cityError` is undefined.
            break;

        case "removed":
            // The document was removed from Firestore.
            // The `city` variable is null.
            // `cityError` is undefined.
            break;

        case "error":
            // An error occurred while fetching the document from Firestore.
            // `city` is undefined.
            // `cityError` contains the Error thrown by Firestore.
            break;
    }
}

This is just a rough skeleton. A real implementation would return an appropriate child component for each case in the switch statement.

The useDocListener hook does two things:

  1. It starts a listener for the specified Firestore document and returns the document data (or an error).
  2. It establishes a lease on the document data.

As long as there is at least one component holding a lease, the document data will remain in a local cache and be updated when changes occur.

The first argument to useDocListener is the name of the lease holder. This name can be anything, but it is a best practice to use the name of the component.

The second argument is the path to the target document expressed as an array of strings. In this example, “cities” is the name of the Firestore collection and cityId is the document id, so ["cities", cityId] is the path to the document.

When the component unmounts, the useEffect hook releases all of the component’s leases.

Next Steps