Firebase:snapshot.val 不是函数或其返回值不可迭代

Posted

技术标签:

【中文标题】Firebase:snapshot.val 不是函数或其返回值不可迭代【英文标题】:Firebase: snapshot.val is not a function or its return value is not iterable 【发布时间】:2019-08-10 22:53:52 【问题描述】:

所以我正在使用 Express、Firebase 和 ReactJS 创建一个小型应用程序。 我偶然发现了 Firebase snapshot.val 的问题

express 上的这部分代码是通过 ID 为我的旅行提供服务并获取所有信息。对于所有旅行都很好,但是当我尝试去单次旅行时,它会返回这个错误

如果我将它解构为一个数组:

UnhandledPromiseRejectionWarning: TypeError: snapshot.val is not a function or its return value is not 可迭代

如果我将它解构为一个对象:

TypeError: 无法解构“未定义”的属性 trip 或 '空'。

我完全感到困惑,找不到解决方案。

这里是 express 部分代码:

const firebase = require('firebase');

const reference = () => firebase.database().ref('/trips')

exports.getSingle = async (id) => 
  //const snapshot = await reference()
  //  .orderByChild('id')
  //  .equalTo(id)
  //  .limitToFirst(1)
  //  .once('value');

  // const [trip] = snapshot.val();
  //  return trip
const snapshot = await reference()
    .orderByChild('id')
    .equalTo(parseInt(id, 10))
    .limitToFirst(1)
    .once('value');
  let trip = [];
  snapshot.forEach(function (tripSnapshot) 
    tripStorage = tripSnapshot.val();
    console.log(tripStorage);
    trip.push(tripStorage);
  );
  console.log(trip);
  return trip;
;

exports.getAll = async () => 
  const snapshot = await reference().once('value');

  return snapshot.val();
;

我在前端获取旅行:

 componentDidMount() 
    fetch("/api/trips/get/" + this.state.id)
      .then(res => res.json())
      .then(trip =>
        this.setState(
          trip
        , () => console.log("Trips fetched...", trip))
      );
  

旅行控制器:

const express = require('express');

const route = express.Router();
const tripUtil = require('../utils/tripUtil');

route.get('/getAll', async (req, res) => 
  const trips = await tripUtil.getAll();

  res.json(trips);
);

route.get('/get/:id', async (req, res) => 
  const  id  = req.params;
  const trip = await tripUtil.getSingle(id);

  if (trip) 
    res.json(trip);
   else 
    res.status = 400;
    res.json(
      error: 'trip not found'
    );
  
);

module.exports = route;

并像这样从对象映射属性:

 this.state.trip.itinerary.days.map((day, i) => 
     return (
       <div key=i className="trip-single-item__day">
          <div className="trip-single-item__day-header">
              <div className="trip-single-item__day-title">
                 day.title
              </div>
              <div className="trip-single-item__day-subtitle">
                 day.subtitle
              </div>
              <div className="trip-single-item__day-date">
                 day.date
              </div>
          </div>
          <div className="trip-single-item__day-body">
             <div className="trip-single-item__day-descritpion">
               day.description
             </div>
          </div>
      </div>
      );
  )

这里是单程图片Single trip in firebase

有什么建议或方向吗?谢谢

【问题讨论】:

为什么将trip 常量放在方括号中? const [trip] = snapshot.val(); 您是否尝试从snapshot.val() 访问属性trip,例如const trip = snapshot.val(); 如果我按照你的建议放置它,那么我会得到 UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property trip of 'undefined' or 'null'。但我认为这是解构 firebase 对象的正确方法? 这只是一个问题,而不是一个建议 :) 你能编辑你的帖子并写下错误吗:it returns me this error and I am totally baffled 完成,感谢您的提示。我为什么要这样做?当我想一想它是否作为对象存储在 firebase 中时,没有逻辑可以通过方括号访问它,但现在我更加困惑为什么行程未定义。 id 属性是单个 trip 对象的嵌套属性吗?如果没有,你可以这样访问它:reference().child(id).once('value')` 【参考方案1】:

Firebase 通常建议不要将数据作为数组存储在实时数据库中(尽管存在一些例外情况):https://firebase.googleblog.com/2014/04/best-practices-arrays-in-firebase.html

我猜是因为某种原因,您的快照值没有被呈现为数组,而是一个带有数字键的对象。造成这种情况的一个原因可能是 Firebase 中的键不是严格按顺序排列的,因为 Firebase 只会在数据的键以 0 开头且没有间隙的情况下将您的数据呈现为数组。

你能试试这个代码看看它是否有效吗?

exports.getSingle = async (id) => 
  const snapshot = await reference()
    .orderByChild('id')
    .equalTo(id)
    .limitToFirst(1)
    .once('value');

  const queryResult = snapshot.val()
  console.log(queryResult) // Make note of the object shape here. Make sure there are actually results.

  const trip = Object.values(queryResult)[0]
  return trip;
;

【讨论】:

它只是将 queryResult 返回为 null,因此对数据库的引用似乎不好。我还编辑了按 ID 服务的控制器代码,也许我在那里遗漏了什么? 似乎 .orderByChild、.equalTo 和 limitToFirst 有问题,我不明白为什么。 您从 Express 获得并传递给 getSingle:id 参数很可能是一个字符串,而 Firebase 中行程的 id 属性是一个数字。这可能会导致您的 equalTo 查询失败,因为它找不到 id === "1" 的项目。试试equalTo(parseInt(id, 10)) 看看能不能解决。 是的,这解决了带有 id 的部分,这显然是一个字符串。但是,在我不得不更新将snapshot.val() 推送到trip 数组的代码之后,发生了相当奇怪的事情,并且推送的元素在推送之前在JSON 中呈现良好,但是当推送里面的嵌套项目时显示为[object Object]。 当使用 console.log 登录到控制台时,深度嵌套的 JSON 对象通常会显示为 [Object] 或 [object Object]。这个是正常的。您可以尝试console.log(JSON.stringify(trips, null, 2)) 序列化对象并将其漂亮地打印为字符串。【参考方案2】:

可能是 UnhandledPromiseRejectionWarning。

用 then/catch 处理你的快照

exports.getSingle = async (id) => 
  await reference()
    .orderByChild('id')
    .equalTo(id)
    .limitToFirst(1)
    .once('value').then(snapshot => 

         const [trip] = snapshot.val();
         return trip

        //  console.log(post.id)  if required
    )
;

【讨论】:

是的,它给出了 UnhandledPromiseRejectionWarning 我已经编辑了错误。正在查看 firebase 文档并尝试了一些您发布的变体,但仍然给我同样的错误。

以上是关于Firebase:snapshot.val 不是函数或其返回值不可迭代的主要内容,如果未能解决你的问题,请参考以下文章

错误“snapshot.val”不是 Google Cloud Functions 中的函数

Firebase JavaScript 快照获取值

注册时无法将个人资料信息添加到 Firebase 集合中(vue | firebase)

solr 可以返回函数值(不是 solr 分数或文档字段)吗?

如何从 Firebase 存储中删除 Vue.js 组件图像?

Angular 2 Firebase Observable 承诺不会返回任何东西