【问题标题】:How to avoid unnecessary Firestore reads in reactjs?如何避免在 reactjs 中不必要的 Firestore 读取?
【发布时间】:2021-04-11 22:21:49
【问题描述】:

免责声明:我希望我的描述一针见血,因为我在这个问题上花了很多时间。请在 cmets 中给我反馈以改进我的问题,以便您有足够的信息给我一个好的答案。我构建这个应用程序是为了支持一个电​​晕疫苗接种中心作为一个慈善项目,但我没有得到报酬,我尽量避免花钱阅读 Firestore。因此我想减少它们。


我有两个使用 ReactJS 构建的网络应用程序:

  1. 用户可以生成签到票
  2. 管理员可以签入用户。

1. 应用程序只有 Firestore 写入。每个用户一次写入。我不会更详细地介绍该应用程序,因为它不会有问题。

2. 应用程序在每个用户签入时有 1 次写入,而我在读取时遇到问题,因为应用程序 currentyl 很快就达到了每天 50.000 次免费读取。而且我只有 3000 个用户(蓝线是读,紫线是写):

Admin 应用具有三个视图:CheckInViewUserArchiveUserView。该应用程序使用带有一个 users 集合的 Firestore 数据库。我构建应用程序只是为了工作,现在我想改进代码以避免对 firestore 应用程序进行不必要的读取。

对于CheckInView,我需要.onSnapshot(),以便管理员无需手动刷新页面并自动查看新用户。它只读取未签入的用户 (.where("checkedin", "==", null))。

UserArchive 应显示已签入的所有 用户 (.where("checkedin", "!=", null))。它有一个输入字段来过滤/搜索特定用户。每天大约有 200-300 个用户,最多 10 个用户不同时签到。这意味着每天有 200-300 个新用户。因此,每天的阅读量增加了 200-300 次。

对于UserView,我再次阅读,但只有一个用户。我不太确定为什么我的应用程序如此努力地扩展读取。当您从UserView 回到UserArchive 时,可能会发生这种情况,因为它一次又一次地获取所有用户。

我可以在UserArchive 中从.onSnapshot() 切换到.get(),但我不确定是否可以通过问题解决。也许不会。

也许有人比我在 firestore 读取方面更有经验,并且可以检查我的代码并发现这么多读取的问题。

这是我的具有基本功能的 mutch 缩小代码:

import { useEffect, useState } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import firebase from "./firebase";

function App() {
  return (
    <div className="App">
      <Router>
        <Switch>
          <Route exact path="/checkin" component={CheckInView} />
          <Route exact path="/userview/:udi" component={UserView} />
          <Route exact path="/userarchive" component={UserArchive} />
        </Switch>
      </Router>
    </div>
  );
}

export default App;

export const CheckInView = () => {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    const unsubscribe = firebase
      .firestore()
      .collection("users")
      .where("infected", "==", null)
      .onSnapshot((fetchedUsers) => {
        setUsers(fetchedUsers.docs.map((user) => user.data()));
      });

    return () => {
      unsubscribe();
    };
  }, [setUsers]);

  return (
    <div>
      <Link to="/userarchive">- Archive</Link>
      {users.map((user) => (
        <div key={user.uid}>
          <Link to={`/userview/${user.uid}`}>{user.name}</Link>
        </div>
      ))}
    </div>
  );
};

const UserArchive = () => {
  const [users, setUsers] = useState([]);
  const [search, setSearch] = useState("");

  useEffect(() => {
    const unsubscribe = firebase
      .firestore()
      .collection("users")
      .where("infected", "!=", null)
      .onSnapshot((fetchedUsers) => {
        setUsers(fetchedUsers.docs.map((user) => user.data()));
      });

    return () => {
      unsubscribe();
    };
  }, [setUsers]);

  return (
    <div>
      <Link to="/checkin">- CheckIn</Link>
      <input
        type="text"
        value={search}
        placeholder="Search..."
        onChange={(e) => setSearch(e.target.value)}
      />
      {users
        .filter((e) => e.name.toLowerCase().includes(search.toLowerCase()))
        .map((user) => (
          <div key={user.uid}>
            <Link to={`/userview/${user.uid}`}>{user.name}</Link>
          </div>
        ))}
    </div>
  );
};

const UserView = ({ match }) => {
  const [user, setUser] = useState();

  useEffect(() => {
    const unsubscribe = firebase
      .firestore()
      .collection("users")
      .doc(match.params.uid)
      .onSnapshot((user) => {
        setUser(user.data());
      });

    return () => {
      unsubscribe();
    };
  }, [setUser, match.params.uid]);

  return (
    <>
      <div>{user !== undefined ? user.name : "No Valid User."}</div>
      <Link to="/checkin">Back</Link>
    </>
  );
};

one .onSnapshot() 添加到 App 组件并将其传递给所有视图并根据我的要求对其进行过滤是个好主意,例如:

  • users.fiter(e =&gt; e.checkedin === true) 用户存档
  • users.fiter(e =&gt; e.checkedin === false) 用于 CheckInView
  • users.fiter(e =&gt; e.uid === march.params.uid) 用于用户视图

【问题讨论】:

    标签: reactjs firebase google-cloud-firestore


    【解决方案1】:

    您好,您阅读的原因是,您的 effect 函数在每次渲染组件时运行。

    例如

    const UserView = ({ match }) => {
      const [user, setUser] = useState();
    
      useEffect(() => {
        const unsubscribe = firebase
          .firestore()
          .collection("users")
          .doc(match.params.uid)
          .onSnapshot((user) => {
            setUser(user.data());
          });
    
        return () => {
          unsubscribe();
        };
      });
    
      return (
        <>
          <div>{user !== undefined ? user.name : "No Valid User."}</div>
          <Link to="/checkin">Back</Link>
        </>
      );
    };
    

    此组件将第一次渲染,效果将运行以获取 user snapshot 然后 setUser => 通过更新您的状态,组件将再次渲染 => 如果您的组件渲染,效果将再次运行。

    这就是为什么你在阅读方面获得了很多荣誉。

    所以你可以通过转动你的效果来调整只运行一次

    useEffect(() => {
        const unsubscribe = firebase
          .firestore()
          .collection("users")
          .doc(match.params.uid)
          .onSnapshot((user) => {
            setUser(user.data());
          });
    
        return () => {
          unsubscribe();
        };
      }, []);
    

    改成上面的,效果只会在组件挂载时运行

    【讨论】:

    • useEffect钩子也依赖于match.params.uid所以最好在依赖数组[match.params.uid]中传递它
    • 抱歉,这是我这边的问题。在我的真实应用程序中,所有 useEffects 都取决于相关的setUsersmatch.params.uid。我更新了我的代码。所以有了这个,我仍然能得到那么多的阅读量。
    • 如果是这样,我认为您应该登录效果函数以检测完整的端到端流程调用了多少次。通过这样做,您将检测到部分调用服务最多
    猜你喜欢
    • 2019-03-13
    • 1970-01-01
    • 2020-03-27
    • 1970-01-01
    • 2011-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    相关资源
    最近更新 更多