【发布时间】:2020-12-17 21:34:53
【问题描述】:
我正在尝试弄清楚如何从 react 中读取 firestore 子集合数据。
我已经看到this blog 描述了如何做到这一点,现在我正试图弄清楚如何实现它的逻辑。我还看到 this gist 提出了一种将父 ID 分配给查询的子集合的方法。
我也一直在尝试理解这个blog post,我认为这与我想要做的非常接近(它查找用户集合 ID 来填充要呈现的项目子集合数据列表)。我认为解决这个问题的关键是弄清楚如何制作这个函数的版本,但是我没有监控 url 参数,而是尝试在查找该词汇表文档的相关术语的钩子中使用特定的glossaryTerm.id:
我有一个名为词汇表的父集合和一个名为relatedTerms的子集合。
我有一个表单可以正确地将数据提交到词汇表(父)和相关术语(子)集合。
我正在努力寻找一种方法来使用反应钩子生成词汇表术语列表(此集合包含数据)以及相关的相关术语子集合数据)。
我目前的尝试是:
function useGlossaryTerms() {
const [glossaryTerms, setGlossaryTerms] = useState([])
// First useEffect - to set the glossaryTerm (the parent document)
useEffect(() => {
async function fetchGlossaryData() {
await firebase
.firestore()
.collection("glossary")
.orderBy('term')
.onSnapshot(snapshot => {
const glossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setGlossaryTerms(glossaryTerms)
});
fetchGlossaryData();
};
// setRelatedGlossaryTerms(glossaryTerms)
}, [])
return glossaryTerms;
};
function useRelatedGlossaryTerms() {
const [relatedGlossaryTerms, setRelatedGlossaryTerms] = useState([])
const glossaryTerms = useGlossaryTerms()
console.log("try logging from inside relatedTerms function", glossaryTerms)
// Second useEffect - to set the relatedGlossaryTerm (the sub collection document)
useEffect(() => {
async function fetchRelatedGlossaryData() {
await firebase
.firestore()
.collection("glossary")
.doc([I'm trying to find a way to populate this field with the id of the glossary doc used in the map])
//.doc("JsSQ3Qf0A65ixNbF07G2") works to render the placeholder text but without the subcollection document data.
//.doc("glossary.id") gets ignored
// {console.log("testing:", glossaryTerms)}
.collection('relatedTerms')
//.where("glossaryId", "==", "glossaryTerms.id")
**** 这是错误的,但我找不到可行的方法。我想我正在尝试询问词汇表的状态,然后是 id 相关对象” ****
// .where("glossaryId", "==", "JsSQ3Qf0A65ixNbF07G2"
**** 这也是错误的,但我不明白为什么。如果上述尝试是不可能的,因为glossaryTerms 是一个对象,那么 这个问题的元素应该通过使用存储在 子集合文档。它不起作用。它不会产生任何 控制台错误,我仍然找不到记录值的方法 子集合。” ****
.onSnapshot(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
});
fetchRelatedGlossaryData();
}
// setRelatedGlossaryTerms(glossaryTerms)
}, [glossaryTerms])
return relatedGlossaryTerms;
};
const GlossaryTerms = () => {
const glossaryTerms = useGlossaryTerms()
const relatedGlossaryTerms = useRelatedGlossaryTerms()
const classes = useStyles();
return (
<div className={classes.root}>
{glossaryTerms.map(glossaryTerm => {
{console.log('testing log of glossaryTerms', glossaryTerms)}
return (
<div key={glossaryTerm.id}>
<Typography className={classes.heading}>{glossaryTerm.term}</Typography>
<div>
{glossaryTerm.category.map(category => (
<Typography className={classes.secondaryHeading}>
{category.label}
</Typography>
)
)}
<div>
<Typography variant="subtitle2" className={classes.heading2}>Meaning</Typography>
<Typography>{glossaryTerm.definition}</Typography>
</div>
{console.log("testing log of glossaryTerm", glossaryTerm)}
- 这可以在地图中记录单个词汇表术语。
{console.log("testing log of relatedGlossaryTerm", relatedGlossaryTerms)}
- 这将返回一个空数组。这对我来说没有任何意义。上面日志中返回的id的glossaryTerm有一个子集合 (称为relatedTerms)与应用于填充数据 relatedGlossaryTerms 状态。
<div>
{relatedGlossaryTerms ? (
<ul>
{relatedGlossaryTerms.map(relatedGlossaryTerm => (
<div key={relatedGlossaryTerm.id}>
<Typography variant="caption">
Placeholder title {relatedGlossaryTerm.title} |
Placeholder description {relatedGlossaryTerm.description}
</Typography>
</div>
))}
</ul>
) : null}
{
glossaryTerm.templates ? (
<div>
<p><Typography variant="caption" >Related Templates</Typography></p>
{glossaryTerm.templates.map(template => (
<Link to="" className="bodylinks" key={template.id}>
<p><Typography variant="caption" >{template.title}
</Typography></p>
</Link>
))}
</div>
) : null
}
</div>
<div>
)
})}
</div>
</div>
</div>
);
}
export default GlossaryTerms;
当我尝试这个时,我在控制台中没有收到任何错误,但是呈现的输出不正确且令人困惑。
词汇表当前在该集合中有 2 个文档。其中一个文档有一个 relatedTerms 子集合,它也有 2 个文档。另一个没有子集合条目(除了我添加到 setSubmitting 表单 add 的glossaryId 字段,以便我可以尝试匹配字符串 id 以过滤子集合文档 - 这种尝试在这篇文章的历史中有所概述并且不起作用。我认为它可能需要组集合查询 - 但我认为这种方法对于从父 ID 访问单个子集合不是必需的)。
虽然正确呈现了词汇表数据,但根本没有呈现相关术语的子集合数据 - 但是占位符文本针对父集合的每个实例(2 个文档)重复。我目前有 2 个。如果我在子集合中添加另一个条目来测试这一点,则在每个呈现的词汇表文档中都会重复 2 个占位符文本集。
下图显示了词汇表集合的内容。
下图显示了relatedTerms子集合的内容。
这很令人困惑,因为占位符文本的重复次数与父集合文档条目的数量相同。它不是测试子集合值。
这也是不正确的,因为我找不到将相关术语加入其相关词汇表集合条目的方法。列表中的每个项目都应仅呈现其子集合内容。
当我尝试记录relatedGlossaryTerms时,如下:
{console.log("testing log of relatedGlossaryTerms", relatedGlossaryTerms)}
{relatedGlossaryTerms ? (
<ul>
{relatedGlossaryTerms.map(relatedGlossaryTerm => (
我明白了:
这个日志让我很困惑,因为它应该是子集合的内容。相反,它会记录父集合。
我找不到记录relatedGlossaryTerms 值的方法。
当我尝试将控制台日志语句添加到快照时,我得到了我无法理解的错误。许多其他帖子表明这是一种用于记录值的正确方法。我尝试添加花括号,但遇到了同样的问题。
useEffect(() => {
firebase
.firestore()
.collection("glossary")
.doc()
// {console.log("testing:", glossaryTerms)}
.collection('relatedTerms')
// .where("glossaryId" === "SQ3Qf0A65ixNbF07G2")
.onSnapshot(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
// console.log('test useEffect setting for relatedGlossaryTerms', doc),
// console.log(relatedGlossaryTerms)
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
});
当我尝试在 return 语句中记录glossaryTerms 和relatedGlossaryTerms 的值时,我得到:
我尝试了 Yoruba 的建议,即使用 get 而不是快照。虽然我不知道为什么我不能在没有 mobx 的情况下使用快照,但我试过了。
useEffect(() => {
firebase
.firestore()
.collection("glossary")
.orderBy('term')
.get()
.then(snapshot => {
const glossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setGlossaryTerms(glossaryTerms)
});
// setRelatedGlossaryTerms(glossaryTerms)
}, [])
return glossaryTerms;
useEffect(() => {
firebase
.firestore()
.collection("glossary")
.doc()
// {console.log("testing:", glossaryTerms)}
.collection('relatedTerms')
// .where("glossaryId" === "SQ3Qf0A65ixNbF07G2")
.get()
.then(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
});
// setRelatedGlossaryTerms(glossaryTerms)
}, [glossaryTerms])
return relatedGlossaryTerms;
};
这没有任何区别。没有新的控制台错误。与上面概述的渲染问题没有区别。
之前的所有尝试
onSubmit={(values, { setSubmitting }) => {
setSubmitting(true);
// firestore.collection("glossary").doc().set({
// ...values,
// createdAt: firebase.firestore.FieldValue.serverTimestamp()
// })
// .then(() => {
// setSubmitionCompleted(true);
// });
// }}
const newDocRef = firestore.collection("glossary").doc() // auto generated doc id saved here
let writeBatch = firestore.batch();
{console.log("logging values:", values)};
writeBatch.set(newDocRef,{
term: values.term,
definition: values.definition,
category: values.category,
context: values.context,
createdAt: firebase.firestore.FieldValue.serverTimestamp()
});
writeBatch.set(newDocRef.collection('relatedTerms').doc(),{
// dataType: values.dataType,
// title: values.title,
glossaryId: newDocRef.id,
...values.relatedTerms
// description: values.description,
})
writeBatch.commit()
.then(() => {
setSubmitionCompleted(true);
});
}}
当我尝试这个时没有共享错误消息,glossaryId 只是被忽略。批处理概念是什么对我来说仍然是个谜——我认为它只有在所有指令都可以执行的情况下才会运行。
我试图弄清楚如何访问存储在子集合中的数据。我目前的尝试是基于上面链接的帖子中的示例。
import React, { useState, useEffect } from 'react';
import {Link } from 'react-router-dom';
import Typography from '@material-ui/core/Typography';
import firebase, { firestore } from "../../../../firebase.js";
import { makeStyles } from '@material-ui/core/styles';
import clsx from 'clsx';
const useStyles = makeStyles((theme) => ({
root: {
width: '100%',
marginTop: '8vh',
marginBottom: '5vh'
},
}));
function useGlossaryTerms() {
const [glossaryTerms, setGlossaryTerms] = useState([])
const [relatedGlossaryTerms, setRelatedGlossaryTerms] = useState([])
useEffect(() => {
firebase
.firestore()
.collection("glossary")
.orderBy('term')
.onSnapshot(snapshot => {
const glossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setGlossaryTerms(glossaryTerms)
});
// setRelatedGlossaryTerms(glossaryTerms)
}, [])
return glossaryTerms;
}
const relatedTermsList = (glossaryTerm) => {
setGlossaryTerms(glossaryTerm);
firebase.firestore().collection('glossary').doc(glossaryTerm.id).collection('relatedTerms').get()
.then(response => {
const relatedGlossaryTerms = [];
response.forEach(document => {
const relatedGlossaryTerm = {
id: document.id,
...document.data()
};
relatedGlossaryTerms.push(relatedGlossaryTerm);
});
setRelatedGlossaryTerms(relatedGlossaryTerms);
})
.catch(error => {
// setError(error);
});
}
const GlossaryTerms = () => {
const glossaryTerms = useGlossaryTerms()
const relatedGlossaryTerms = useGlossaryTerms()
const classes = useStyles();
return (
<div>
{glossaryTerms.map(glossaryTerm => {
return (
{glossaryTerm.term}
{glossaryTerm.category.map(category => (
{category.label}
)
)}
</div>
{glossaryTerm.definition}
{glossaryTerm ? (
<ul>
{relatedGlossaryTerms.map(relatedGlossaryTerm => (
<li key={relatedGlossaryTerm.id}>
{relatedGlossaryTerm.title} | {relatedGlossaryTerm.description}
</li>
))}
</ul>
) : null}
)
})}
</div>
);
}
export default GlossaryTerms;
当我尝试这个时,我收到错误消息,指出在我的 relatedTermsList const 中,setGlossaryTerms 和 setRelatedGlossaryTerms 的定义未定义。
这些错误中的每一个对我来说都很奇怪,因为 setRelatedGlossaryTerms 的定义方式与 setGlossaryTerms 相同。我不明白为什么它无法识别并且 setGlossaryTerms 在 useEffect 中使用没有任何问题。
下一次尝试
我尝试使用第二个使用词汇表术语的 useEffect 函数。
function useGlossaryTerms() {
const [glossaryTerms, setGlossaryTerms] = useState([])
const [relatedGlossaryTerms, setRelatedGlossaryTerms] = useState([])
useEffect(() => {
firebase
.firestore()
.collection("glossary")
.orderBy('term')
.onSnapshot(snapshot => {
const glossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setGlossaryTerms(glossaryTerms)
});
// setRelatedGlossaryTerms(glossaryTerms)
}, [])
return glossaryTerms;
}
useEffect((glossaryTerm) => {
firebase
.firestore()
.collection("glossary")
.doc(glossaryTerm.id)
.collection('relatedTerms')
.onSnapshot(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
})
.catch(error => {
// setError(error);
});
});
const GlossaryTerms = () => {
const glossaryTerms = useGlossaryTerms()
const relatedGlossaryTerms = useGlossaryTerms()
const classes = useStyles();
return (
我不明白为什么,但错误是 setRelatedGlossaryTerms 未定义 - 它在第二个 useEffect 中使用。
下一次尝试
此尝试不会在控制台中产生任何错误,但结果不正确。
我有两个useEffect函数如下:
function useGlossaryTerms() {
const [glossaryTerms, setGlossaryTerms] = useState([])
const [relatedGlossaryTerms, setRelatedGlossaryTerms] = useState([])
useEffect(() => {
firebase
.firestore()
.collection("glossary")
.orderBy('term')
.onSnapshot(snapshot => {
const glossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setGlossaryTerms(glossaryTerms)
});
// setRelatedGlossaryTerms(glossaryTerms)
}, [])
return glossaryTerms;
useEffect((glossaryTerms) => {
firebase
.firestore()
.collection("glossary")
.doc("glossaryTerms.id") //I also tried .doc(glossaryTerms.id)
.collection('relatedTerms')
.onSnapshot(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
});
// setRelatedGlossaryTerms(glossaryTerms)
}, [])
return relatedGlossaryTerms;
};
const GlossaryTerms = () => {
const glossaryTerms = useGlossaryTerms()
const relatedGlossaryTerms = useGlossaryTerms()
const classes = useStyles();
return (
这两个useEffects背后的意图是第一个找到glossaryTerm,然后第二个使用glossaryTerm.id查找是否有任何相关术语,然后使用任何相关术语设置relatedGlossaryTerms数组的状态。
然后在渲染中,我有:
{glossaryTerm.category.map(category => (
<Typography className={classes.secondaryHeading}>
{category.label}
</Typography>
)
以上方法可以正确找到词汇表。
{relatedGlossaryTerms ? (
<ul>
{relatedGlossaryTerms.map(relatedGlossaryTerm => (
<div>
<Typography variant="caption">
Placeholder title {relatedGlossaryTerm.title} |
Placeholder description {relatedGlossaryTerm.description}
</Typography>
</div>
))}
</ul>
) : null}
上面的目的是为了看看有没有相关的GlossaryTerms(应该是在glossaryTerm里面找到子集合的结果。
这不会产生任何控制台错误,但有两件事需要纠正。
-
它为每个词汇表呈现相同的结果(不管它实际上是否有任何相关的词汇表。
-
它不呈现相关的GlossaryTerm 数据。下图显示了重复打印的占位符文本(次数等于所有glossaryTerms 文档中相关术语的总和)。
我正在尝试找到一种方法来使用glossaryTerm 文档ID 来查找子集合数据并仅在相关的glossaryTerm 项内呈现它。
下一次尝试
我也试过了:
useEffect((glossaryTermsId) => {
firebase
.firestore()
.collection("glossary")
.doc(glossaryTermsId)
.collection('relatedTerms')
.where(glossaryTermsId === "glossaryId") // I also tried .where(glossaryTerms.id === "glossaryId)
.onSnapshot(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
});
我在子集合中添加了一个名为glossaryId 的字段,以尝试使用where 查询来过滤子集合(尽管这有效,但它没有)- 我不想搜索每个glossaryTerm 文档获取正确的相关条款。
useEffect((glossaryTerms) => {
firebase
.firestore()
.collection("glossary")
.doc(glossaryTerms)
.collection('relatedTerms')
.where("glossaryId" === "JsSQ3Qf0A65ixNbF07G2")
.onSnapshot(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
});
我仍然得到如上图所示的相同结果(只是占位符文本打印 - 所有词汇表中的相同方式 - 它只是忽略了子集合文档中的词汇表 ID 属性的查询参数。
下一次尝试
我正在为现在尝试的事情寻找想法。我尝试将glossaryTerms 添加为第二个useEffect 的依赖项,并尝试将glossaryTermsId 作为prop(而不是glossaryTerms 对象)提供,如下所示:
useEffect((glossaryTermsId) => {
firebase
.firestore()
.collection("glossary")
.doc(glossaryTermsId)
.collection('relatedTerms')
// .where("glossaryId" === "JsSQ3Qf0A65ixNbF07G2")
.onSnapshot(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
});
// setRelatedGlossaryTerms(glossaryTerms)
}, [glossaryTerms])
return relatedGlossaryTerms;
};
这些都不会对上面显示的结果产生任何影响。
当我尝试在渲染中记录词汇表的内容时 - 一件奇怪的事情是子集合数据没有显示。我在词汇表集合中只有 2 个测试条目。 1有一个相关的术语,而另一个没有。在控制台日志中都不可见。我认为这是因为浅查询。但是,我找不到将集合查询链接到子集合数据的方法。
下一次尝试
听从 Bennett 的建议,我将 useEffect 中的参数去掉如下:
useEffect(() => {
firebase
.firestore()
.collection("glossary")
.doc()
// {console.log("testing:", glossaryTerms)}
.collection('relatedTerms')
// .where("glossaryId" === "JSQ3Qf0A65ixNbF07G2")
.onSnapshot(snapshot => {
const relatedGlossaryTerms = snapshot.docs.map(doc => ({
id: doc.id,
...doc.data(),
}))
setRelatedGlossaryTerms(relatedGlossaryTerms)
});
// setRelatedGlossaryTerms(glossaryTerms)
}, [glossaryTerms])
return relatedGlossaryTerms;
};
当我尝试这个时,它仍然呈现与上图所示相同的输出。
当我取消注释 where 查询(这是具有相关术语的子集合的文档的文档 ID 时,我希望能够访问子集合数据(即使它在所有词汇表术语中,而不是只是子集合所属的那个)。
相反,我得到占位符文本,呈现的次数等于所有词汇表文档中所有子集合中记录的相关术语的总数(但没有数据)。
然后,当我更改字符串时,它应该评估为 null,我得到相同的渲染输出(仍然打印占位符)。
【问题讨论】:
-
您的基本设置存在一个大问题:
useEffect将永远无法为其回调提供参数,例如。 G。useEffect((glossaryTermsId) => {glossaryTermsId将始终是undefined并在useEffect的代码中被忽略,这将导致许多其他问题。即使您在useEffect之外定义了同名的变量,它也会被useEffect的回调覆盖,因此它在useEffect本身中是未定义的。删除您正在使用的每个useEffect上的回调参数(如glossaryTermsId)。 codesandbox.io/s/little-hill-bikps -
感谢@BennettDams。这很有帮助。我从第二次使用效果中删除了参数。我不知道如何获取子集合数据。你知道我在哪里可以找到如何在反应钩子中做到这一点的例子吗?再次感谢。
-
我不确定您指的是哪个“第二次使用效果”,但如果它是您第一个示例中的那个(并且您删除了参数),则以下内容将不起作用:
.doc("glossaryTerms.id"),因为glossaryTerms是一个数组而不是一个对象。我认为,如果您使用一种特定设置来更新您的问题以进行讨论,这将很有帮助,您的所有尝试都很难遵循。你也可以通过每一行和console.log你正在使用的每个变量,所以很清楚问题到底发生在哪里。 -
我认为您必须在没有监听器 onSnapshot 的情况下从 firestore 获取数据。而是使用一次获取数据firebase.google.com/docs/firestore/query-data/get-data。如果你想使用监听器,最好使用mobx-state-tree.js.org 之类的商店
-
感谢@BennettDams - 我在这篇文章中添加了一个新的开场白,它只使用了当前的尝试。我试过记录相关词汇表的值,但它返回词汇表的值——这很奇怪。关于我可以在哪里寻找尝试的任何想法?再次感谢您。
标签: javascript reactjs firebase google-cloud-firestore react-hooks