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')错误。
问题原因
- 查询层级错误:当前查询的是
food节点的直接子节点(如Bakery、Beverage),但这些分类节点本身没有ShopNo字段,ShopNo存在于分类节点的子节点(即具体店铺)中,导致orderByChild("ShopNo")无法匹配到任何数据。 - 随机数范围不匹配:原代码生成的随机数范围是
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
相关产品推荐
相关产品推荐

