如何使用 React 从 firestore 文档呈现单个字段

How to render single field from firestore document with React

对于我们的大学项目(第 4 学期),我们正在 React 中构建一个测验应用程序,使用 MaterialUI 进行样式设置并使用 Firestore 作为数据库。

我目前正在努力处理来自 firestore 文档的 displaying/rendering 数据。

firestore 集合称为用户,每个注册的用户都会获得一个以 his/her 电子邮件作为 ID 的文档。作为字段,我们将使用名字、姓氏、电子邮件和主题,但目前只填充电子邮件。

在我们的个人资料页面上,我们现在想显示所述用户数据(现在是电子邮件),但是在加载应用程序时,该元素留空。

import React, {useState, useEffect} from "react";
import "../App.css";
import Typography from "@material-ui/core/Typography";
import {
  createStyles,
  makeStyles,
  Container,
  Card,
  CardContent,
} from "@material-ui/core";
import { PageTitle } from "../components/common";
import { db , auth} from "../firebase";

const useStyles = makeStyles((theme) =>
createStyles({
  proflileCard: {
      minWidth: 200,
  },
  spaceLG: {
    paddingRight: theme.spacing(30)
  },
  spaceXL: {
    paddingTop: theme.spacing(10),
  },
})
);


function Profile() {
  const classes = useStyles();
  const user = auth.currentUser
  const userEmail = user.email
  const userRef = db.collection('user')
  
  
  function displayMail(){

   
    userRef.where("email", "==" , userEmail).get().then(function(querySnapshot) {
      querySnapshot.forEach(function(doc) {
        
        console.log(doc.id, " => ", doc.data().email);
        const data = doc.data()
        const mail = data.email  

        return mail
        
      });
    })
    .catch(function(error) {
      console.log("Fehler beim Abrufen des Dokuments", error)
    })  
  }
  
  return (
    < >
        <PageTitle title="Profil" />
    
        <Card 
        className={classes.profileCard} 
        variant='outlined'>
          <CardContent>

              <Typography 
            className={classes.spaceLG} 
            variant='h5'> 
              {displayMail()}
            </Typography> 
           
            
          </CardContent>
        </Card>
         
    </>
    
  );
}

是否有我缺少的 React 或 firestore 功能?

感谢您观看我们的烂摊子:)

更新

我可以在与朋友交谈并阅读 useState 和 useEffect 说明后解决此问题。 我从函数切换到 useEffect 并将 querySnapshot 替换为简单的 documentSnapshot。

这成功了:

function Profile() {
  const classes = useStyles();
  const user = auth.currentUser
  const userEmail = user.email
  const userRef = db.collection('user').doc(userEmail)
  const [mail, setMail] = useState('')    
  
  useEffect(() => {

    
    userRef.onSnapshot(docSnapshot => {

      try {
        console.log(docSnapshot.id, " => ", docSnapshot.data().email);
        const data = docSnapshot.data()
        const mail = data.email 

        setMail(mail)
        
      } catch(error) {
          console.log("Fehler beim Abrufen des Dokuments", error)
        } 
    })    

  }, [])
  
  
  return (
    <div>
        <PageTitle title="Profil" />
    
        <Card 
        className={classes.profileCard} 
        variant='outlined'
        >
          <CardContent>

              <Typography 
            className={classes.spaceLG}
            variant='h5' 
            > 
              Login-Data
            </Typography> 
            <Typography>
              <strong>E-Mail: </strong>{mail}
            </Typography>
       //[...]
       </div>
         
 )

问题是您正在使用 Firebase SDK,就像它是同步的一样。您需要注意它的 async 性质,最好使用 state 来处理它。您的代码可能如下所示:

import React, { useState, useEffect } from "react";
import "../App.css";
import Typography from "@material-ui/core/Typography";
import {
  createStyles,
  makeStyles,
  Container,
  Card,
  CardContent,
} from "@material-ui/core";
import { PageTitle } from "../components/common";
import { db, auth } from "../firebase";

const useStyles = makeStyles((theme) =>
  createStyles({
    proflileCard: {
      minWidth: 200,
    },
    spaceLG: {
      paddingRight: theme.spacing(30),
    },
    spaceXL: {
      paddingTop: theme.spacing(10),
    },
  })
);

function Profile() {
  const classes = useStyles();
  const [mails,setMails] = useState([]);
  const user = auth.currentUser;
  const userEmail = user.email;
  const userRef = db.collection("user");

  function displayMail() {
    userRef
      .where("email", "==", userEmail)
      .get()
      .then(function (querySnapshot) {
        const tempMails=[]
        querySnapshot.forEach(function (doc) {
          console.log(doc.id, " => ", doc.data().email);
          const data = doc.data();
          const mail = data.email;
          tempMails.push(mail);

          
        });
        setMails(tempMails);

      })
      .catch(function (error) {
        console.log("Fehler beim Abrufen des Dokuments", error);
      });
  }

  return (
    <>
      <PageTitle title="Profil" />

      <Card className={classes.profileCard} variant="outlined">
        <CardContent>
          <Typography className={classes.spaceLG} variant="h5">
            {mails.map(m=>{
              return <div>{m}</div>)})}
          </Typography>
        </CardContent>
      </Card>
    </>
  );
}