如何 return 从 Firebase 到 React 组件的值?

How to return a value from Firebase to a react component?

我正在尝试从 Firebase 上的 RealtimeDatabase 读取一个值并将其呈现在一个元素中,但它保持 returning 未定义。我有以下代码:

const getStudentName = (studentId) => {
    firebase.database().ref('students').child(studentId).on("value", (snapshot) => {
        return snapshot.val().name; 
    })
}

const StudentName = (studentId) => ( <p>{getStudentName(studentId)}</p> )

我知道数据库本身或我找到的值都没有问题,因为如果我这样做:

const getStudentName = (studentId) => {
    firebase.database().ref('students').child(studentId).on("value", (snapshot) => {
        console.log(snapshot.val().name);
        return "Test"; 
    })
}

我仍然看到我的数据库中的正确名称按预期输出到控制台,但“测试”未 return 编辑到元素。但是,如果我这样做:

const getStudentName = (studentId) => {
    firebase.database().ref('students').child(studentId).on("value", (snapshot) => {
        console.log(snapshot.val().name);
    })
    return "Test"; 
}

然后“Test”被return编辑到

元素并显示。我很困惑,因为我不明白我的 console.log() 是如何在函数内部到达的,但是紧随其后的 'return' 语句不会 return.

React 和 Firebase 新手,请帮忙!谢谢。

编辑:我确定它是不言自明的,但您可以假设一个简单的数据库,其形式为:

{ "students": [
    "0": { "name": "David" },
    "1": { "name": "Sally" } ]}

如果'studentId'为0则'console.log(snapshot.val().name)'成功输出'David',但'David'不会return到

元素。

你不能 return 这样的异步调用。如果你检查调试器或添加一些日志记录,你会看到你的外部 return "Test"console.log(snapshot.val().name) 被调用之前运行。

在 React 中,您需要使用 useState 钩子(或 setState 方法)来告知 React 新值,以便它可以重新渲染 UI .

我建议阅读 using the state hook, and the documentation on setState 上的 React 文档。

我不确定您在哪里使用 getStudentName,但是您当前的代码使 real-time 监听器附加到该数据库位置。每次该位置的数据更新时,都会调用您的回调函数。因此,从这样的函数返回一个值没有多大意义。

如果您只想从数据库中获取名称一次,则可以使用 once() 方法,其中 returns 一个 Promise 包含您要查找的值。

作为另一个小优化,如果您只需要学生的姓名,请考虑取回 /students/{studentId}/name

const getStudentName = (studentId) => {
    return firebase.database()
      .ref("students")
      .child(studentId)
      .child("name")
      .once("value")
      .then(nameSnapshot => nameSnapshot.val());
}

使用上面的代码,getStudentName(studentId) 现在 returns 一个 Promise<string | null>,其中 null 将在该学生不存在时返回。

getStudentName(studentId)
  .then(studentName => { /* ... do something ... */ })
  .catch(err => { /* ... handle errors ... */ })

如果您正在填充 <Student> 组件,继续使用 on 快照侦听器可能是更好的选择:

const Student = (props) => {
  const [studentInfo, setStudentInfo] = useState({ status: "loading", data: null, error: null });

  useEffect(() => {
    // build reference
    const studentDataRef = firebase.database()
      .ref("students")
      .child(props.studentId)
      .child("name");

    // attach listener
    const listener = studentDataRef.on(
      'value',
      (snapshot) => {
        setStudentInfo({
          status: "ready",
          data: snapshot.val(),
          error: null
        });
      },
      (error) => {
        setStudentInfo({
          status: "error",
          data: null,
          error
        });
      }
    );

    // detach listener in unsubscribe callback
    return () => studentDataRef.off(listener);
  }, [props.studentId]); // <- run above code whenever props.studentId changes

  // handle the different states while the data is loading
  switch (studentInfo.status) {
    case "loading":
      return null; // hides component, could also show a placeholder/spinner
    case "error":
      return (
        <div class="error">
          Failed to retrieve data: {studentInfo.error.message}
        </div>
      );
  }

  // render data using studentInfo.data
  return (
    <div id={"student-" + props.studentId}>
      <img src={studentInfo.data.image} />
      <span>{studentInfo.data.name}</span>
    </div>
  );
}

由于您最终可能会经常使用上述 useState/useEffect 组合,您可以将其重写到您自己的 useDatabaseData 钩子中。