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

2024-01-20

我正在尝试弄清楚如何从 React 中读取 firestore 子集合数据。

我见过这个博客 https://dev.to/rossanodan/how-to-fetch-subcollections-from-cloud-firestore-with-react-3n93它描述了如何做到这一点,现在我正在尝试找出如何实现它的逻辑。我也见过这个要点 https://gist.githubusercontent.com/mo-sharif/bdd2cdfdb5b36d2501d2668227c840b8/raw/f2f4db3087381d2fe0b712ff74f5e5f5ad5be43f/s它提供了一种将父 id 分配给子集合 where 查询的方法。

I have also been trying to understand this blog post https://blog.logrocket.com/react-hooks-with-firebase-firestore/, which I think is very close to what I am trying to do (it looks for the user collection id to populate the list of items subcollection data to render). I think the key to solving this problem is figuring out how to make a version of this function, but instead of monitoring a url parameter, I'm trying to use the particular glossaryTerm.id in the hook that looks for that glossary document's relatedTerms: enter image description here

我有一个名为“glossary”的父集合,其中有一个名为“relatedTerms”的子集合。

我有一个表单可以正确地将数据提交到词汇表(父)和相关术语(子)集合。

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

My 目前的尝试这样做的目的是:

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" 

**** 这也是错误的,但我不明白为什么。如果上述尝试由于lossaryTerms是一个对象而无法实现,那么 这个问题的元素应该通过使用存储的字符串来解决 子集合文档。这不起作用。它不会生成任何 控制台错误,我仍然找不到记录值的方法 子集合。”****

          .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)以及应用于填充的数据 相关词汇表术语状态。
                    <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表单添加的glossaryId字段之外,以便我可以尝试匹配字符串id来过滤子集合文档 - 此尝试在本文的历史记录中概述,并且不起作用我认为它可能需要组集合查询 - 但我不认为这种方法对于从父 id 访问单个子集合是必要的。

虽然词汇表数据已正确呈现,但 relatedTerms 的子集合数据根本不会呈现 - 但占位符文本会针对父集合(2 个文档)的每个实例重复。我目前有 2 个。如果我向子集合中添加另一个条目来测试这一点,则这 2 个占位符文本集将在呈现的每个术语表文档中重复。

The image below shows the rendered output. enter image description here

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

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

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

这也是不正确的,因为我找不到将 relatedTerm 添加到其相关的lossaryTerm 集合条目的方法。列表中的每个项目应仅呈现其子集合内容。

当我尝试记录相关术语表时,如下所示:

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

I get:

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

我找不到记录 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 Trying Yoruba's (now deleted) suggestion

我尝试了约鲁巴的建议,使用 get 而不是 snapshot。虽然我不明白为什么没有 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 没有任何问题。

下一次尝试

我尝试使用第二个带有lossaryTerm的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 背后的意图是,第一个 useEffects 查找lossaryTerm,然后第二个 useEffects 使用lossaryTerm.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}

上面的目的是查看是否有任何相关的术语表术语(这应该是在术语表术语中找到子集合的结果。

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

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

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

我正在尝试找到一种方法来使用lossaryTerm文档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)
  });

我在子集合中添加了一个名为lossaryId的字段,以尝试使用where查询来过滤子集合(尽管即使这有效,但它不起作用) - 我不想搜索每个glossaryTerm文档来获取正确的相关条款。

enter image description here When I try

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中的方式相同 - 它只是忽略子集合文档中的glossaryId属性的查询参数。

下一次尝试

我现在正在寻找尝试的想法。我尝试将lossaryTerms添加为第二个useEffect的依赖项,并尝试将glossaryTermsId作为道具(而不是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;
};

这些事情对上面显示的结果都没有任何影响。

当我尝试在渲染中记录lossaryTerm的内容时,一件奇怪的事情是子集合数据没有显示。我的词汇表集合中只有 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,该文档具有 relatedTerms 的子集合)时,我期望能够获取子集合数据(即使它在所有词汇表术语中,而不仅仅是一个)子集合所属的集合)。

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

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


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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在react中读取firestore子集合数据 - 如何在子集合where查询中设置父id 的相关文章

随机推荐