在react中读取firestore子集合数据-如何在查询的子集合中设置父ID

时间:2020-08-29 00:50:26

标签: javascript reactjs firebase google-cloud-firestore react-hooks

我正在尝试找出如何从react读取firestore子集合数据。

我已经看到this blog描述了如何执行此操作,现在正试图弄清楚如何实现其逻辑。我还看到了this gist,它阐明了一种将父ID分配给查询子集的方法。

我也一直试图理解这个blog post,我认为它与我正在尝试做的非常接近(它寻找用户集合ID来填充要呈现的项子集合数据的列表)。我认为解决此问题的关键是弄清楚如何创建此函数的版本,但我没有监视URL参数,而是尝试在用于查找词汇表文档相关条款的挂钩中使用特定的lossaryTerm.id: enter image description here

我有一个称为词汇表的父集合,在其中有一个名为relatedTerms的子集合。

我有一个可以正确地将数据提交到词汇表(父级)和relatedTerm(子级)集合的表格。

我正在努力寻找一种方法来使用react挂钩来生成词汇表术语列表(该集合包含数据)以及相关的相关术语子集合数据。)

我目前的尝试

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

****这是错误的,但我找不到可行的方法。我想我想询问词汇表术语的状态,然后询问 相关对象“ ****

//              .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为词汇表的术语有一个子集合 (称为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访问单个子集合不需要这种方法。

正确表达了词汇表数据后,根本没有呈现relatedTerms的子集合数据-但是,对父集合的每个实例(2个文档)重复占位符文本。我目前有2个。如果我在子集合中添加另一个条目以进行测试,则在每个呈现的词汇表文档中都会重复2个占位符文本集。

下图显示了渲染的输出。 enter image description here

下图显示了词汇表集合的内容。

enter image description here

下图显示了relatedTerms子集合的内容。

enter image description here

这令人困惑,因为占位符文本的重复次数与父集合文档条目的重复次数相同。它不在测试子集合值。

这也是不正确的,因为我找不到将relatedTerm与其相关的词汇表术语集合条目结合起来的方法。列表中的每个项目都应仅呈现其子集合内容。

当我尝试记录relatedGlossaryTerm时,如下所示:

 {console.log("testing log of relatedGlossaryTerms", relatedGlossaryTerms)}
                    {relatedGlossaryTerms ? (
                      
                      <ul>
                        {relatedGlossaryTerms.map(relatedGlossaryTerm => (
                    

我得到:

enter image description here

此日志令我感到困惑,因为它应该是子集合的内容。而是记录父集合。

我找不到一种方法来记录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语句中记录lossaryTerms和relatedGlossaryTerms的值时,我得到:

enter image description here enter image description here 尝试Yoruba的建议(现已删除)

我尝试了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相同。我不明白为什么它无法识别,并且在useEffect中使用setGlossaryTerms没有任何问题。

下一个尝试

我尝试使用第二个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,然后第二个使用lossaryTerm.id查找是否有任何相关术语,然后使用任何相关术语设置relatedGlossaryTerms数组的状态。 / p>

然后在渲染中,我有:

 {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(这应该是在词汇表术语内找到子集合的结果。

这不会产生任何控制台错误,但是它有两件事需要更正。

  1. 它为每个词汇表术语呈现相同的结果(无论它是否实际上具有任何相关的词汇表术语。

  2. 它不呈现relatedGlossaryTerm数据。下图显示了重复打印的占位符文本(其次数等于所有词汇表术语文档中相关术语的总数)。

enter image description here

我正在尝试找到一种方法来使用词汇表术语id来查找子集合数据,并将其仅在相关的词汇表术语内呈现。

下一次尝试

我也尝试过:

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

我已在子集合中添加了一个名为lossaryId的字段,以尝试使用where查询来过滤子集合(尽管即使该方法可行,但它也不起作用)-我不想搜索每个lossaryTerm文档获得正确的relatedTerms。

enter image description here 当我尝试

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

我仍然得到与上图所示相同的结果(只是占位符文本打印出-在所有lossaryTerms中都是相同的方式-它只是忽略了子集合文档中lossaryId属性的查询参数。

下一个尝试

我正在努力寻找可尝试的想法。我尝试将glossaryTerms添加为对第二个useEffect的依赖项,并尝试提供glossaryTermsId作为道具(而不是lossaryTerms对象),如下所示:

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

这一切都不会对上面显示的结果产生任何影响。

当我尝试将renderaryTerm的内容记录在渲染器中时-一件奇怪的事情是未显示子集合数据。我的词汇表集中只有2个测试条目。 1有一个相关术语,而另一个则没有。在控制台日志中都不可见。我认为这是由于查询浅。但是,我无法找到一种方法来将集合查询链接到子集合数据。

enter image description here

下一次尝试

根据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查询(这是具有relatedTerm的子集合的文档的doc id时,我希望能够获得subcolleciton数据(即使在所有词汇表术语中,而不是只是子集合所属的那个)。

相反,我得到占位符文本,其渲染次数等于所有词汇表术语文档中所有子集合中记录的相关术语总数(但没有数据)。

然后,当我更改字符串时,它的值应为null,我得到相同的渲染输出(仍打印占位符)。

2 个答案:

答案 0 :(得分:2)

我确实尝试过使用像db这样的真实数据,但是您无法列出文档的子集合。 我认为最好的方法是为relatedTerms创建一个单独的集合,并在保存到数据库期间将表单数据保存到2个单独的集合中。 或使用firebase函数在后端构建视图模型,然后将其发送到视图。

https://firebase.google.com/docs/firestore/query-data/get-data#web_6

enter image description here

答案 1 :(得分:0)

这有效,但是每个词汇表子视图都被加载。 此外,每个词汇表孩子都将找到该数据库。 这就是为什么最好将relatedTerms放入单独的集合中。 然后,您可以通过1 db调用获得词汇表集合,并通过1 db调用获得相关术语。

  const [glossaryTerms, setGlossaryTerms] = useState([])
  const [relatedGlossaryTerms, setRelatedGlossaryTerms] = useState([])

  useEffect(() => {
    // get the glossary collection
   firebase.firestore()
   .collection("glossary").orderBy('term').get().then(glossaries => {
      let tempGlossaryTerms = []
      let tempRelatedGlossaryTerms = []

      glossaries.forEach(doc => {
        const glossary = {id: doc.id, ...doc.data()}
        tempGlossaryTerms.push(glossary)

        // for each glossary get the related term
        firebase.firestore()
          .collection("glossary")
          .doc(glossary.id)
          .collection('relatedTerms')
          .get().then(relatedTerms => {
            relatedTerms.docs.forEach(doc => {
              const relatedTerm = {id: doc.id, ...doc.data()}
              tempRelatedGlossaryTerms.push(relatedTerm)
            })
            setRelatedGlossaryTerms(tempRelatedGlossaryTerms)
          })

      })
      setGlossaryTerms(tempGlossaryTerms)
    })
  }, [])