使用Cloud Firestore和ReactJS的最佳实践

发布于 2025-01-21 21:13:23 字数 712 浏览 0 评论 0 原文

更具体地说,我询问了国家管理以及向Firestore阅读和写作的最佳实践。在开始设置并为我正在使用的网站进行布局后,我开始想知道这一点。我目前对Firestore的了解是,从集合或文档中获取数据的最基本方法是使用GET( )方法是接收一次读取或使用OnSnapShot()接收文档的实时数据流。由于这些不同的方法,这使我想到了设置组件和状态逻辑的最佳方法。

  1. 您最初可以调用get()方法并将其设置为等于组件状态。然后,您可以使用该状态将数据传递给组件和儿童。要编写/删除数据,您可以同时写入/删除状态和Firestore文档。这似乎具有有限阅读的好处,但更多的写作。它似乎还增加了组件状态和实际文档之间的DESANC的可能性。

  2. 为了减少写入的数量,是否可以继续写入/删除该州,而不是同时使用Firestore文件?相反,一旦组件卸下后,您是否可以使用使用效应返回函数来编写/删除状态和文档之间的差异?此选项似乎有限的读取,有限的写作,并且具有单一的数据来源(状态)。但是,卸载文档写入的逻辑可能更复杂。

  3. 似乎也可以使用onSnapShot()方法删除并发写入/删除状态,并仅将它们放在文档上。似乎每次都可以直接从该文档中聆听文档的更改并更新状态。此选项似乎具有简单的状态逻辑的好处,并且国家与文件之间没有脱节。但是依次似乎会导致很多读写和写作。

我没有发现太多的信息直接讨论这些想法,我对处理与上述示例相关的数据流和州管理的当前最佳实践感到好奇。任何帮助将不胜感激!

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 a a 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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

空城旧梦 2025-01-28 21:13:23

创建上下文对于应用程序,

首先,我们将为Firestore Access和Firestore访问和上下文创建一个上下文使用其提供商包装应用程序 authProvider ,我们将创建一个hook useauth 访问必要的方法或访问firestore -db,

让我们从创建上下文开始

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 };

,对于挂钩,我们将简单地做 -

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

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

export default useAuth;

现在我们已经创建了已准备好下一步的挂钩

:在您的登录屏幕中使用 signin 以进行严格访问,签名

”以下面的组件(PS使用 React> React> React> React-Router-dom 路由),可以从 数据,

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;
}

现在我们将从我们的组件(路由)上访问firestore(路由

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>
   )
)

)很乐意在评论中解决您的疑问:)

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 (p.s 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 :)

染火枫林 2025-01-28 21:13:23

我也正在用React Web应用中的Firestore管理服务器端状态的最佳方法 - 尤其是鉴于公告并发react

我最终写了自己的国家管理库。

您可能需要查看@gmcfall/react-firebase-state
。它使用听众来确保数据是新鲜的,并且面对许多渲染周期和多个安装/卸载周期,它具有弹性。

你写的

..似乎(onsnapshot)会引起很多读写和写入。

我的经验恰恰相反。我必须更少去服务器的旅行,以确保我的数据是新鲜的。该解决方案的关键是要从持有服务器端状态的本地高速缓存中拥有组件“租赁”数据。但是,当数据索赔数量为零时,实体不会立即被驱逐出缓存。这样,如果用户短暂地远离组件,然后返回,或者同时渲染启动,则数据仍然存在,并且保证它是最新的。

I, too, was wrestling with the best way to manage server-side state from Firestore in a react web app -- especially in light of the announcement about Concurrent React.

I ended up writing my own state-management library.

You might want to check out @gmcfall/react-firebase-state
. It uses listeners to ensure that data is fresh, and it is resilient in the face of many render cycles and multiple mount/unmount cycles.

You wrote that

.. it seems like (onSnapshot) would cause a lot of reads and writes.

My experience is just the opposite. I have to make fewer trips to the server to ensure that my data is fresh. The key to this solution is to have components "lease" data from a local cache that holds server-side state. But entities don't get evicted from the cache immediately when the number of claims on the data goes to zero. This way, if the user navigates away from the component briefly and then returns, or if concurrent rendering kicks in, the data will still be there, and it is guaranteed to be up-to-date.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文