我正在努力了解Cloud Firestore读取配额是如何工作的。我已经阅读了这篇文章和对它的回应。我的控制台是打开的,但我无法理解一个包含3个文档的集合是如何构成一个";繁忙控制台";。
我很难理解这些文档。
我在消防仓库有两个收藏品。每个都有3份文件。每个文档有2个属性。我正在使用这些属性填充"材质UI"中"自动完成"选择菜单中的选项。
在localhost中,我一直在运行该表单,以测试使用单个快照将这些属性放入自动完成选择菜单。在30秒内,这两个表单项目在firestore中产生了1.1万次阅读。
我想:
-
快照仅在firestore中的数据发生更改时更新。
-
通过使用取消订阅,钩子将停止侦听firestore中的更改。
-
通过将列表的状态添加为挂钩的依赖项(useEffect块末尾的orgList(,提高了firestore读取的效率:https://medium.com/javascript-in-plain-english/firebase-firestore-database-realtime-updates-with-react-hooks-useeffect-346c1e154219.
有人能看到通过仅使用2个输入项运行此表单生成1.1k读数的情况吗(目前整个应用程序中没有其他firestore调用(。
import React, { useState, useEffect } from 'react';
import Checkbox from '@material-ui/core/Checkbox';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import firebase from "../../../../../firebase";
const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
export default function CheckboxesTags() {
const [orgList, setOrgList] = useState([]);
const [selectedOrgList, setSelectedOrgList] = useState();
const [loading, setLoading ] = useState(true);
const [ error, setError ] = useState(false);
useEffect(() => {
// if (doc.exists) {
const unsubscribe = firebase
.firestore()
.collection("organisations")
.onSnapshot((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
shortName: doc.data().shortName,
location: doc.data().location
}));
setOrgList(orgList);
console.log("orglist", orgList)
}, () => {
setError(true)
});
setLoading(false);
return() => unsubscribe();
}, [orgList]);
return (
<div>
<Autocomplete
multiple
id="orgList options"
options={orgList}
disableCloseOnSelect
getOptionLabel={(option) => option.shortName}
renderOption={(orgList, { selected }) => (
<React.Fragment>
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
style={{ marginRight: 8 }}
checked={selected}
/>
{orgList.shortName} <span style={{marginRight: "4px", marginLeft: "4px"}}>-</span>
{orgList.location}
</React.Fragment>
)}
style={{ width: 500 }}
renderInput={(params) => (
<TextField {...params}
variant="outlined"
label="Select Organisation"
placeholder="Acme Inc."
/>
)}
/>
</div>
);
}
另一个表单与此完全相同,但它没有使用orgList,而是使用userList。否则,一切都是一样的(所以:2个集合,每个集合中有3个文档,每个文档中有2个属性(。
const [orgList, setOrgList] = useState([]);
const [selectedOrgList, setSelectedOrgList] = useState();
const [loading, setLoading ] = useState(true);
const [ error, setError ] = useState(false);
useEffect(() => {
// if (doc.exists) {
const unsubscribe = firebase
.firestore()
.collection("organisations")
.onSnapshot((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
shortName: doc.data().shortName,
location: doc.data().location
}));
setOrgList(orgList);
console.log("orglist", orgList)
}, () => {
setError(true)
});
setLoading(false);
return() => unsubscribe();
}, [orgList]);
根据我的理解,我们告诉React,如果orgList
发生变化,请运行此效果。效果如下:
- 致电Firestore
- 映射<-这导致了这个问题,因为我们正在创建一个新的数组
setOrgList(orgList)
<-这就是问题所在
现在orgList已经更改,React必须重新运行效果。我创建了一个类似的stackblitz(来自材料ui的主页(,它可能会导致这个问题。看见https://stackblitz.com/edit/evsxm2?file=demo.js.查看控制台并注意到它一直在运行。
解决此问题的可能方法
如果我们只需要一次数据。。。
建议1:在开始使用时设置if条件效果
const [orgList, setOrgList] = useState([]);
const [selectedOrgList, setSelectedOrgList] = useState();
const [loading, setLoading ] = useState(true);
const [ error, setError ] = useState(false);
useEffect(() => {
if (orgList.length > 0) {
return; // we already have data, so no need to run this again
}
const unsubscribe = firebase
.firestore()
.collection("organisations")
.onSnapshot((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
shortName: doc.data().shortName,
location: doc.data().location
}));
setOrgList(orgList);
console.log("orglist", orgList)
}, () => {
setError(true)
});
setLoading(false);
return() => unsubscribe();
}, [orgList]);
建议2如果我们真的需要倾听实时变化。。。?(我还没有测试过(
const [orgList, setOrgList] = useState([]);
const [selectedOrgList, setSelectedOrgList] = useState();
const [loading, setLoading ] = useState(true);
const [ error, setError ] = useState(false);
useEffect(() => {
const unsubscribe = firebase
.firestore()
.collection("organisations")
.onSnapshot((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
shortName: doc.data().shortName,
location: doc.data().location
}));
setOrgList(orgList);
}, () => {
setError(true)
});
setLoading(false);
return() => unsubscribe();
}, []); // we don't depend on orgList because we always overwrite it whenever there's a snapshot change
orgList
不应在useEffect
钩子中声明为依赖项,您真正想要的是setOrgList
。
我相信你在这里触发了一个无限循环,因为钩子每次CCD_;更新的";在钩子内部,重新触发它。然而,它从来没有在钩子内部使用过,看起来这真的不是你想要的。如果您想仅在"上设置快照侦听器;安装件";然后简单地使用一个空的依赖关系列表或者研究记忆策略。你可能想要的是:
useEffect(() => {
const unsubscribe = firebase
.firestore()
.collection("organisations")
.onSnapshot((snapshot) => {
const orgList = snapshot.docs.map((doc) => ({
id: doc.id,
shortName: doc.data().shortName,
location: doc.data().location
}));
setOrgList(orgList);
setLoading(false);
}, () => {
setError(true)
setLoading(false);
});
return () => unsubscribe();
}, []); // <----- empty dependency means it only runs on first mount
编辑:
可能的困惑是,你认为如果orgList
内部的数据看起来相同,那么React应该知道不要重新触发,然而useEffect
并不像你想象的那么聪明,所以你必须自己做一些工作来帮助它。由于orgList
是一个对象,它实际上是一个引用,并且这个引用正在重复更新。关于te(按值(与引用(按参考(的一些潜在澄清:JavaScript(按引用(与(按值
对许多人有效的解决方案是使用缓存,而不是不断地从Firestore读取。
例如,直接从Firebase文档
var getOptions = {
source: 'cache'
};
// Get a document, forcing the SDK to fetch from the offline cache.
docRef.get(getOptions).then(function(doc) {
// Document was found in the cache. If no cached document exists,
// an error will be returned to the 'catch' block below.
console.log("Cached document data:", doc.data());
}).catch(function(error) {
console.log("Error getting cached document:", error);
});
这篇文章的内容也很丰富,并且有一个代码示例(虽然是Java for Android(,我发现它对理解如何减少对Firestore的读取量非常有用。
对于其他想要学习的人,我刚刚发现了这篇博客文章,它也有助于理解依赖数组:https://maxrozen.com/learn-useeffect-dependency-array-react-hooks/