TLDR;
@react-firebase was released to make integrating Firebase in your React App fast & easy π.
If you use React & Firebase, go check it out !
The longer version
I β€οΈ Firebase & React but getting them to work together on relatively large codebases with other contributors tends to lead to :
- π Un-necessary complexity in your codebase
- π Subtle bugs with subscription management and inter-resource dependencies.
- π Zombie listeners sticking around when your UI doesn't need them.
@react-firebase offers small, typed, well-tested composable components that allow easy integration of Firebase Auth, Realtime Database and/or Firestore in your React or React Native app.
Overview
@react-firebase/auth, @react-firebase/database, @react-firebase/firestore export Provider and Consumer Components.
Providers
The Provider components take in firebase and firebase config options props and should be rendered once in your app as an ancestor to your Consumer components.
How do I get my Firebase config ?
Web or React Native with Expo
If you're using firebase
Change PROJECT_NAME to your project name and grab your firebase config here :
https://console.firebase.google.com/project/PROJECT_NAME/settings/general/
It should look something like this :
// Firebase Config
const config = {
  apiKey: "API_KEY",
  projectId: "PROJECT_ID",
  databaseURL: "DATABASE_URL",
  authDomain: "AUTH_DOMAIN",
  // OPTIONAL
  storageBucket: "STORAGE_BUCKET",
  messagingSenderId: "MESSAGING_SENDER_ID"
};
React Native Firebase
If you're using react-native-firebase then you only need to pass in your version of firebase as a prop (the firebase app is already initialized on the native side.)
Provider Examples
import * as firebase from "firebase/app";
import "firebase/auth";
import { FirebaseAuthProvider } from "@react-firebase/auth";
// And or
import { FirebaseDatabaseProvider } from "@react-firebase/database";
import "firebase/database";
// And or
import "firebase/firestore";
import { FirestoreProvider } from "@react-firebase/firestore";
<FirebaseAuthProvider firebase={firebase} {...config}>
  <MyApp1 />
  <MyApp2 />
</FirebaseAuthProvider>
Consumers
Every Module exports Consumer components that will consume and react (π ) to Firebase changes.
All Consumer Components expect their children prop to be a React Node or a sync function that returns a React Node. (library-specific examples below π)
Firebase Auth Consumers
  
  
  FirebaseAuthConsumer
Anywhere inside your app component tree, add a FirebaseAuthConsumer to listen and update the ui when the user's auth status changes.
<FirebaseAuthConsumer>
  {({ isSignedIn, user, providerId }) => {
    return isSigned ? (
      <pre style={{ height: 300, overflow: "auto" }}>
        {JSON.stringify({ isSignedIn, user, providerId }, null, 2)}
      </pre>
    ) : <div>Not signed in.</div>;
  }}
</FirebaseAuthConsumer>
Auth API Reference
Firebase Database Consumers
Read Data
  
  
  FirebaseDatabaseNode
Anywhere inside your app component tree, add a FirebaseDatabaseNode to listen and update the ui when the data changes at the provided path.
<FirebaseDatabaseNode path={"user_bookmarks/BOOKMARK_ID"}>
  {({ value, isLoading }) => {
    return (
      <pre>
        {JSON.stringify({ value, isLoading })}
      </pre>
    );
  }}
</FirebaseDatabaseNode>
You can also query your Firebase data by using all supported Firebase Realtime Database operators :
<FirebaseDatabaseNode path={"user_bookmarks/BOOKMARK_ID"} limitToFirst={10} orderByKey>
  {({ value, isLoading }) => {
    return (
      <pre>
        {JSON.stringify({ value, isLoading })}
      </pre>
    );
  }}
</FirebaseDatabaseNode>
Write Data
  
  
  FirebaseDatabaseMutation & FirebaseDatabaseTransaction
The mutation API is inspired by apollo-graphql mutations.
The mutation components inject a runMutation or runTransaction prop, respectively to their children.
FirebaseDatabaseMutation, in addition to the path prop requires a mutation type prop that can be one of :
"set" | "update" | "push";
FirebaseDatabaseMutation Example.
<FirebaseDatabaseMutation path={collectionPath} type="push">
  {({ runMutation }) => (
    <button
      onClick={async () => {
        const { key } = await runMutation(testDocValue);
        if (key === null || typeof key === "undefined") return;
        this.setState(state => ({
          keys: [...state.keys, key]
        }));
      }}
    >
      add-node-with-generated-key
    </button>
  )}
</FirebaseDatabaseMutation>
FirebaseDatabaseTransaction Example.
<FirebaseDatabaseTransaction path={path}>
  {({ runTransaction }) => {
    return (
      <div>
        <button
          onClick={() => {
            runTransaction({
              reducer: val => {
                if (val === null) {
                  return 1;
                } else {
                  return val + 1;
                }
              }
            }).then(() => {
              console.log("Ran transaction");
            });
          }}
        >
          Click me to run a transaction
        </button>
      </div>
    );
  }}
</FirebaseDatabaseTransaction>
Firestore Database Consumers
  
  
  Read Data with FirestoreDocument & FirestoreCollection
Anywhere inside your app component tree, add a FirestoreDocument or FirestoreCollection to listen to the data and update the ui when it changes.
  
  
  FirestoreDocument Example.
const s = v => JSON.stringify(v, null, 2);
const App = () => (
  <FirestoreDocument path={`/user_bookmarks/id`}>
    {({ value, path, isLoading }) => {
      return (
        <div>
          {value && <pre>{s(value)}</pre>}
          {path && <div>{path}</div>}
          <div>{isLoading}</div>
        </div>
      );
    }}
  </FirestoreDocument>
);
  
  
  FirestoreDocument API
FirestoreCollection Example
<FirestoreCollection path="/user_bookmarks/" limit={5}>
  {d => {
    return <pre>{s(d)}</pre>;
  }}
</FirestoreCollection>
  
  
  FirestoreCollection API
  
  
  Write Data with FirestoreMutation & FirestoreTransaction & FirestoreBatchedWrite
The mutation API is inspired by apollo-graphql mutations.
The mutation components FirestoreMutation & FirestoreTransaction inject a runMutation or runTransaction prop, respectively to their children.
FirestoreMutation in addition to the path prop requires a mutation type prop that can be one of :
"set" | "update" | "add";
The BatchedWrite exposes a different API because it behaves differently than writes and transactions, because it can run many updates before committing them.
It injects 2 props, addMutationToBatch and commit, to its children.
- 
addMutationToBatch
type addMutationToBatch = (
  {
    path,
    value,
    type
  }: {
    path: string;
    value: any;
    type: "add" | "update" | "set" | "delete";
  }
) => void;
- 
commit
type commit = () => Promise<void>;
  
  
  FirestoreMutation Example
<FirestoreProvider firebase={firebase} {...config}>
  <FirestoreMutation type="set" path={path}>
    {({ runMutation }) => {
      return (
        <div>
          <h2> Mutate state </h2>
          <button
            onClick={async () => {
              const mutationResult = await runMutation({
                nowOnCli: Date.now(),
                nowOnServer: firebase.firestore.FieldValue.serverTimestamp()
              });
              console.log("Ran mutation ", mutationResult);
            }}
          >
            Mutate Set
          </button>
        </div>
      );
    }}
  </FirestoreMutation>
</FirestoreProvider>
  
  
  FirestoreTranasaction Example
<FirestoreProvider firebase={firebase} {...config}>
  <FirestoreTransaction>
    {({ runTransaction }) => {
      return (
        <div>
          <button
            data-testid="test-set"
            onClick={async () => {
              await runTransaction(async ({ get, update }) => {
                const res = await get({
                  path
                });
                const data = res.data();
                if (typeof data === "undefined") return;
                await update(path, {
                  ...data,
                  a: isNaN(parseInt(data.a, 10)) ? 1 : data.a + 1
                });
              });
            }}
          >
            runTransaction
          </button>
        </div>
      );
    }}
  </FirestoreTransaction>
</FirestoreProvider>
  
  
  FirestoreBatchedWrite Example
<FirestoreBatchedWrite>
  {({ addMutationToBatch, commit }) => {
    return (
      <div>
        <h2>Batched write</h2>
        <button
          onClick={() => {
            console.log("adding to batch");
            addMutationToBatch({
              path,
              value: { [`a-value-${Date.now()}`]: Date.now() },
              type: "set"
            });
          }}
        >
          Add to batch
        </button>
        <button
          onClick={() => {
            console.log("committing to batch");
            commit().then(() => {
              console.log("Committed");
            });
          }}
        >
          Commit batch
        </button>
      </div>
    );
  }}
</FirestoreBatchedWrite>
Resources
Related Work
- re-base
- redux-firebase
- react-firestore
- Your Library ? Send me a DM or comment here to add it.
Thanks for making it to the end of the post β€οΈ !
Here are a couple of additional resources just for you :
 

 
    
Top comments (1)
Hi Rakan,
Thanks for the helpful article.
I noticed in this section it should be
return isSignedIninstead ofisSigned.