Tebo
Tebo

Reputation: 445

Firebase snapshot stored in React Context

I'm starting to work on a brand new app and I have certain amount of data coming from Firestore that's always being used so I figured I'd just add it to React Context and make it available all the time (using a snapshot with real time updates). I've looked around and didn't find something similar so my question is, would this be a valid use case? Or should I be reading data each time a component needs it?

This would be my data.context.jsx file

import { collection, onSnapshot, query } from 'firebase/firestore';
import React, { createContext, useContext, useEffect, useState } from 'react';

import { AuthContext } from '../auth/auth.context';
import { FirebaseContext } from '../firebase/firebase.context';

export const DataContext = createContext();

export const DataContextProvider = ({ children }) => {
  const { db } = useContext(FirebaseContext);
  const { userId } = useContext(AuthContext);

  const [loadingContactList, setLoadingContactList] = useState(false);
  const [contactList, setContactList] = useState(null);
  const [loadingNotesList, setLoadingNotesList] = useState(false);
  const [notesList, setNotesList] = useState(null);

  useEffect(() => {
    if (!userId) return; // No userId, no business

    setLoadingContactList(true);
    console.log('Loading contact list');
    const unsubscribe = onSnapshot(
      query(collection(db, `users/${userId}/contacts`)),
      snapshot => {
        setContactList(
          snapshot.docs.map(doc => {
            return doc.data();
          })
        );
        setLoadingContactList(false);
      },
      err => {
        // TODO: handle errors;
      }
    );

    return () => unsubscribe();
  }, [userId, db, setContactList]);

  useEffect(() => {
    if (!userId) return; // No userId, no business

    setLoadingNotesList(true);
    console.log('Loading notes list');
    const unsubscribe = onSnapshot(
      query(collection(db, `users/${userId}/notes`)),
      snapshot => {
        setNotesList(
          snapshot.docs.map(doc => {
            return doc.data();
          })
        );
        setLoadingNotesList(false);
      },
      err => {
        // TODO: handle errors;
      }
    );

    return () => unsubscribe();
  }, [userId, db, setNotesList]);

  return (
    <DataContext.Provider
      value={{
        loading: loadingContactList || loadingNotesList,
        loadingContactList,
        loadingNotesList,
        contactList,
        notesList,
      }}
    >
      {children}
    </DataContext.Provider>
  );
};

In another app I'm using swr-firestore (https://github.com/nandorojo/swr-firestore) which worked great cause it was holding a data cache but this library hasn't been updated in a long time.

Upvotes: 2

Views: 2206

Answers (1)

Reviewing your project, using snapshot to get realtime updates with Cloud Firestore seems to be a valid use case.

Until the initial onSnapshot event resolves, the hook returns a loading state, which is true by default. It also generates a data array by looping through the documents returned by the snapshot. When an onSnapshot event happens, the re-render is triggered.

An initial call to the callback you give creates an instant document snapshot of the single document's present contents. The document snapshot is then updated every time the contents change.

An alternative solution could be React-Query is frequently referred to as React's missing data-requesting library, but in more technical terms, it makes fetching, caching, syncing, and updating server state in React apps a snap.

You can also review the React useContext Hook documentation.

Upvotes: 2

Related Questions