简体   繁体   中英

Best practices for using Cloud Firestore and Reactjs

More specifically I'm asking about best practices for state management and reading and writing to firestore. I started to wonder about this after starting to set up and layout my data model for a website I'm working on in. My current understanding of firestore is that the most basic ways to get data from a collection or document is using the get() method to receive a one time read, or using onSnapshot() to receive aa real-time data stream of the document. Because of these different methods, it got me thinking about the best way to set up my component and state logic.

  1. You could initially call the get() method and set it equal to component state. You could then use that state to pass data to the component and children. To write/and remove data you could concurrently write/remove both to the state and to the firestore document. This seems to have the benefit of limited reads, but more writes. It also seems to add to the possibility of desync between the component state and the actual document.

  2. To reduce the amount of writes, could it be possible to continue the write/remove to the state, but not the firestore document concurrently? Instead, could you use a useEffect return function to write/remove the difference between the state and the document once the component unmounts? This option seems to have limited reads, limited writes, and having a single source of truth for the data (the state). But, the logic for the unmount document write could be more complicated.

  3. It also seems possible to use the onSnapshot() method to remove the concurrent writes/remove on the state, and have them solely on the document. It seems to be possible to listen to the changes to the document and update the state from that document directly each time. This option seems to have the benefits of easy state logic and no desync between the state and the documents. But in turns it seems like this would cause a lot of reads and writes.

I haven't found too much information discussing these ideas directly, and I was curious on what were the current best practices in dealing with data flow and state management related to the above examples. Any help would be greatly appreciated!

creating the context for the app

first we'll create a context for the firestore access and wrap the App with its provider lets call it AuthProvider and we'll create a hook useAuth to access necessary methods or accessing firestore-db

let's start with creating the context as -

import { initializeApp } from "firebase/app";
import {
  getAuth,
  onAuthStateChanged,
  signInWithEmailAndPassword,
  signOut,
} from "firebase/auth";
import { getFirestore } from "firebase/firestore";
import React from "react";

const firebaseConfig = {
  apiKey: "",
  authDomain: "",
  projectId: "",
  storageBucket: "",
  messagingSenderId: "",
  appId: "",
};

const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
const db = getFirestore(app);

let AuthContext = React.createContext(null);

function AuthProvider({ children }) {
  let [initializing, setInitializing] = React.useState(true);
  let [user, setUser] = React.useState(null);

  let authChanged = React.useCallback((firebaseUser) => {
    if (firebaseUser) setUser(firebaseUser);
    setInitializing(false);

    console.log({ firebaseUser });
  }, []);

  React.useEffect(() => {
    const subscriber = onAuthStateChanged(auth, authChanged);
    return subscriber;
  }, [authChanged]);

  let signin = async (newUser, successCallback, errorCallback) => {
    setInitializing(true);
    try {
      let res = await signInWithEmailAndPassword(
        auth,
        newUser.email,
        newUser.password
      );
      if (res.user) return successCallback();

      return errorCallback("Wrong credentials");
    } catch (error) {
      return errorCallback("Something went Wrong.");
    }
  };

  let signout = async (callback) => {
    await signOut(auth);
    setUser(null);
    callback();
  };

  return (
    <AuthContext.Provider value={{ initializing, user, signin, signout, db }}>
      {children}
    </AuthContext.Provider>
  );
}

export default AuthProvider;
export { AuthContext };

now for the hook we'll simply do -

import React from "react";
import { AuthContext } from "./authProvider";

function useAuth() {
  return React.useContext(AuthContext);
}

export default useAuth;

now that we've created the hook we are ready for the next step

NOTE: make use of signin in your login screen for strict access, and signout

getting along with react-router-dom

wrap all your routes that needs acces to firestore data with below component (ps use react-router-dom for route)

export function RequireAuth({ children }) {
  let auth = useAuth();   // we'll create this hook to manage firbase auth and database access
  let location = useLocation(); // hook from react-router-dom

  if (auth.initializing) return null;

  if (!auth.user)
    return <Navigate to="/login" state={{ from: location }} replace />;

  return children;
}

now we'll access firestore from our hook on components(routes) as

export default function Documents() {
  const [docs, setDocs] = React.useState([]);
  const [docsLoading, setDocsLoading] = React.useState(true);

  let { user, db } = useAuth();

  React.useEffect(() => {
    async function getUserDocuments() {
      setDocsLoading(true);
      const q = query(collection(db, "docs"), where("author", "==", user.uid));
      const querySnapshot = await getDocs(q);
      const tmpDocs = [];
      querySnapshot.forEach((doc) => {
        tmpDocs.push({ id: doc.id, data: doc.data() });
      });
      setDocs(tmpDocs);
      setDocsLoading(false);
    }

    getUserDocuments();
  }, []);

  return !docsLoading ? (
        docs.map((doc) => (/* show in ui */))
   ) : (
      <div>some loader... until we are loading data</div>
   )
)

I'll be more than happy to address your doubts in comments:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM