You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Expo中Firebase实时数据库数据获取错误排查

React Native Expo中Firebase实时数据库查询问题排查

问题场景

在React Native Expo项目中操作Firebase实时数据库时,尝试通过获取food/Total的值生成随机数,再用orderByChild("ShopNo")和equalTo(random)查询对应ShopNo的店铺节点,但始终无法正确获取目标数据:

  • 初始查询返回整个food节点而非目标店铺
  • 后续两种尝试方法要么无数据返回,要么抛出空指针错误

已按要求配置数据库规则,日志可正常输出店铺总数和随机数,但无法获取店铺节点数据。

数据库结构

{
  "Bakery": {
    "Bakery Cuisine": {
      "Description": "Within North Spine Plaza",
      "Halal": "Yes",
      "Latitude": 1.34714,
      "Location": "50 Nanyang Ave, #01-20 North Spine Plaza, Singapore 639798",
      "Longitude": 103.68066,
      "OH": "Mon - Sat : 8 AM to 7 PM, Sun Closed",
      "ShopNo": 1
    }
  },
  "Beverage": {
    "Beverage": {
      "Description": "Within the South Spine food court",
      "Halal": "No",
      "Latitude": 1.34253,
      "Location": "21 Nanyang Link, Singapore 637371",
      "Longitude": 103.68243,
      "OH": "Mon - Fri: 7 30 am to 8 pm, Sat - Sun/PH Closed",
      "ShopNo": 2
    },
    "Beverages": {
      "Description": "Within North Spine Koufu",
      "Halal": "No",
      "Latitude": 1.34708,
      "Location": "76 Nanyang Dr, #02-03 North Spine Plaza, Singapore 637331",
      "Longitude": 103.68002,
      "OH": "Mon - Fri : 7 am to 8 pm, Sat : 7 am to 3 pm, Sun Closed",
      "ShopNo": 3
    },
    "Boost": {
      "Description": "Within North Spine Plaza",
      "Halal": "No",
      "Latitude": 1.34735,
      "Location": "50 Nanyang Ave, #01-11 North Spine Plaza, Singapore 639798",
      "Longitude": 103.68036,
      "OH": "Mon - Fri : 10 am to 9 pm, Sat - Sun: 10 am to 6 pm",
      "ShopNo": 4
    }
  },
  "Total": 89
}

尝试的方法及问题

方法1:直接查询food节点

const SubScreen2 = () => {
  const navigation = useNavigation()
  const [todoData, setToDoData] = useState([])
  
  useEffect(() => {
    var random = 0
    get(ref(db, "food/Total")).then(snapshot => {
      const count = snapshot.val();
      console.log(count)
      random = Math.floor((Math.random() * count));
      console.log(random)
      
      const rc = query(ref(db, `food/`), orderByChild("ShopNo"), equalTo(random))
      get(rc).then((querySnapshot) => {
        querySnapshot.forEach((shopSnapshot) => {
          const shopKey = shopSnapshot.key;
          console.log("Hello")
          console.log("Randomly selected shop: " + shopKey)
          const shopData = shopSnapshot.val();
          console.log("Shop data", shopData);
        })
      }).catch(error => {
        console.log(error);
      })
    })
  }, [])
}

问题:无报错,但仅输出店铺总数和随机数,未获取到任何店铺数据。

方法2:通过辅助函数获取首个匹配节点

const SubScreen2 = () => {
  const navigation = useNavigation()
  const [todoData, setToDoData] = useState([])
  
  function getFirstChild(queryRef) {
    return get(query(queryRef, limitToFirst(1))).then((querySnapshot) => {
      let firstChild = null;
      querySnapshot.forEach((childSnapshot) => {
        firstChild = childSnapshot;
      });
      return firstChild;
    });
  }
  
  useEffect(() => {
    var random = 0
    get(ref(db, "food/Total")).then(snapshot => {
      const count = snapshot.val();
      console.log(count)
      random = Math.floor((Math.random() * count));
      console.log(random)
      
      const rc = query(ref(db, `food/`), orderByChild("ShopNo"), equalTo(random))
      getFirstChild(rc).then((shopSnapshot) => {
        const shopKey = shopSnapshot.key;
        console.log("Randomly selected shop: " + shopKey)
        const shopData = shopSnapshot.val();
        console.log("Shop data", shopData);
      })
    })
  }, [])
}

问题:抛出TypeError: Cannot read properties of null (reading 'key')错误。

问题原因

  1. 查询层级错误:当前查询的是food节点的直接子节点(如Bakery、Beverage),但这些分类节点本身没有ShopNo字段,ShopNo存在于分类节点的子节点(即具体店铺)中,导致orderByChild("ShopNo")无法匹配到任何数据。
  2. 随机数范围不匹配:原代码生成的随机数范围是0~88,但数据库中ShopNo是从1开始的,会出现随机数为0时完全无法匹配的情况。

解决方案

方案1:调整数据库结构(推荐)

将所有店铺直接放在food/shops节点下(移除分类层级),让每个店铺节点直接包含ShopNo字段,结构如下:

{
  "shops": {
    "Bakery Cuisine": {
      "Description": "Within North Spine Plaza",
      "Halal": "Yes",
      "ShopNo": 1,
      // 其他字段...
    },
    "Beverage": {
      "Description": "Within the South Spine food court",
      "Halal": "No",
      "ShopNo": 2,
      // 其他字段...
    }
  },
  "Total": 89
}

调整后的查询代码:

useEffect(() => {
  get(ref(db, "food/Total")).then(snapshot => {
    const count = snapshot.val();
    // 生成1~89的随机数,匹配ShopNo范围
    const random = Math.floor(Math.random() * count) + 1;
    console.log(random);
    
    const queryRef = query(ref(db, "food/shops"), orderByChild("ShopNo"), equalTo(random));
    get(queryRef).then(shopSnapshot => {
      if (!shopSnapshot.empty) {
        shopSnapshot.forEach(shop => {
          console.log("Randomly selected shop: " + shop.key);
          console.log("Shop data", shop.val());
        });
      }
    });
  });
}, []);

方案2:保留原结构,遍历分类查询

如果需要保留原有的分类结构,需要先遍历所有分类节点,再在每个分类下查询匹配ShopNo的店铺:

useEffect(() => {
  get(ref(db, "food/Total")).then(snapshot => {
    const count = snapshot.val();
    const random = Math.floor(Math.random() * count) + 1;
    console.log(random);
    
    // 获取所有分类节点
    get(ref(db, "food/")).then(categorySnapshot => {
      categorySnapshot.forEach(categoryChild => {
        // 遍历每个分类下的店铺
        const categoryPath = `food/${categoryChild.key}/`;
        const queryRef = query(ref(db, categoryPath), orderByChild("ShopNo"), equalTo(random));
        
        get(queryRef).then(shopSnapshot => {
          if (!shopSnapshot.empty) {
            shopSnapshot.forEach(shop => {
              console.log("Randomly selected shop: " + shop.key);
              console.log("Shop data", shop.val());
            });
          }
        });
      });
    });
  });
}, []);

注意事项

  • 确保Firebase数据库规则允许读取food节点下的所有数据;
  • 如果使用方案1,记得同步更新数据写入的逻辑,确保新店铺直接写入food/shops节点;
  • 随机数生成时必须+1,保证范围与ShopNo的起始值匹配。

内容的提问来源于stack exchange,提问作者L.Calvin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 21:35:00