You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Native Expo中从Firebase实时数据库获取随机节点的问题及字段未定义排查

React Native Expo中从Firebase实时数据库获取随机节点的问题及字段未定义排查

嘿,我来帮你一步步梳理这个问题,从最初的随机节点获取失败,到后来的字段显示undefined,咱们逐一解决~

一、最初问题:无法获取随机节点,snapshot.val()[random]返回undefined

先看你最开始的代码和数据库结构,核心问题出在数据获取逻辑错误Firebase查询用法不对

1. 原代码的核心错误点

你最初的代码里,第二个get请求直接拉取food/路径的数据,但这个路径下是分类节点(比如BakeryBeverage),不是你要的店铺节点!而你生成的random是从Total(店铺总数,比如89)来的,范围是0-88,但food/下的分类数量只有2个左右,用这个随机数去取分类数组的索引,肯定会超出范围,得到undefined

另外,Firebase的get方法传查询条件的方式不对,正确的应该用query函数包裹ref和查询规则,而不是把orderByKey()limitToFirst()直接作为get的参数。

2. 数据库结构说明

你的数据库里,店铺是嵌套在分类下的:

{
  "food": {
    "Bakery": {
      "Bakery Cuisine": {
        "Description": "Within North Spine Plaza",
        "Halal": "Yes",
        "Location": "50 Nanyang Ave, #01-20 North Spine Plaza, Singapore 639798",
        "OH": "Mon - Sat : 8 AM to 7 PM, Sun Closed"
      }
    },
    "Beverage": {
      "Beverage": {
        "Description": "Within the South Spine food court",
        "Halal": "No",
        "Location": "21 Nanyang Link, Singapore 637371",
        "OH": "Mon - Fri: 7 30 am to 8 pm, Sat - Sun/PH Closed"
      },
      "Total": 89
    }
  }
}

要拿到随机店铺,得先遍历所有分类,再收集每个分类下的店铺(排除Total节点)。

二、修正后的随机节点获取方案

下面是调整后的代码,能正确收集所有店铺并随机选择一个:

import { StyleSheet, Text, View } from 'react-native'
import { db } from '../firebase'
import React, { useEffect, useState } from 'react'
import { ref, get } from 'firebase/database'

const SubScreen2 = () => {
  // 初始值设为null,避免初始渲染时数组被误判为有数据
  const [todoData, setToDoData] = useState(null)

  useEffect(() => {
    // 1. 先获取food下的所有分类
    get(ref(db, "food"))
      .then((categorySnapshot) => {
        const categories = categorySnapshot.val()
        if (!categories) {
          console.log("没有获取到分类数据")
          return
        }
        const allShops = []
        // 2. 遍历每个分类,收集所有店铺
        for (const categoryKey in categories) {
          const categoryData = categories[categoryKey]
          for (const shopKey in categoryData) {
            // 排除Total节点,只收集店铺
            if (shopKey !== "Total") {
              allShops.push({
                shopName: shopKey, // 保存店铺名称
                ...categoryData[shopKey] // 展开店铺的详细字段
              })
            }
          }
        }
        // 3. 生成随机索引,选中一个店铺
        if (allShops.length === 0) {
          console.log("没有找到任何店铺")
          return
        }
        const randomIndex = Math.floor(Math.random() * allShops.length)
        const randomShop = allShops[randomIndex]
        setToDoData(randomShop)
        console.log("随机选中的店铺:", randomShop)
      })
      .catch((error) => {
        console.log("获取数据出错:", error)
      })
  }, [])

  console.log(todoData)

  return (
    <View style={styles.container}>
      {todoData ? (
        <View>
          <Text>店铺名称:{todoData.shopName}</Text>
          <Text>描述:{todoData.Description}</Text>
          <Text>是否清真:{todoData.Halal}</Text>
          <Text>位置:{todoData.Location}</Text>
          <Text>营业时间:{todoData.OH}</Text>
        </View>
      ) : (
        <Text>Loading...</Text>
      )}
    </View>
  )
}

export default SubScreen2

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  }
})

三、后续问题:字段显示undefined的排查思路

你说用了方案后,DescriptionHalal等字段还是undefined,可能有这几个原因:

  • 字段名大小写不匹配:Firebase实时数据库是区分大小写的,检查你的DB里的字段名(比如Description)和代码里渲染的字段名是否完全一致,有没有写错大小写。
  • 初始值设置问题:如果todoData初始值是[](数组),那渲染时todoData ? ...会认为数组是“有值”的,但数组没有Description等字段,就会显示undefined。把初始值改成null就能解决这个问题,让初始状态显示Loading。
  • 数据权限问题:检查你的Firebase实时数据库规则,确保你的App有读取food节点的权限,否则categorySnapshot.val()会返回null,导致allShops是空数组,最终todoData还是null或者undefined。
  • 误收集了非店铺节点:比如代码里没排除Total或者其他非店铺节点,导致allShops里混入了数值或其他类型的数据,没有对应的字段。可以在收集时加个判断,确保每个节点是包含Description等字段的店铺。

备注:内容来源于stack exchange,提问作者L.Calvin

火山引擎 最新活动