React中如何从查询Firebase的异步函数读取/导出返回数据
问题修复方案
你的代码存在3个核心问题,导致拿不到返回值:
async函数本身返回Promise对象,同步执行时永远会先拿到<pending>状态的Promise,不可能直接同步读到异步返回的结果属性run函数内虽然计算了totalCalories,但没有把计算结果作为函数返回值return到外层,就算Promise执行完成,外层拿到的也是undefined- 原有逻辑只统计了总卡路里,没有收集
mealTime等其他需要的文档字段
第一步:修正firebase.js中的查询方法
不要混用await和.then()链式调用,同时把需要的总卡路里、文档字段统一返回,修正后代码如下:
import moment from 'moment'; // 提前引入已初始化的Firestore db实例 export async function run() { const timeRange = 48; // 不要直接访问moment的私有属性_d,用官方toDate()方法转原生Date对象更稳妥 const startTime = moment().subtract(timeRange, "hours").toDate(); try { const qSnapshot = await db .collection("/food/meals") .where("mealTime", ">=", startTime) .get(); let totalCalories = 0; const mealList = []; qSnapshot.forEach((doc) => { const docData = doc.data(); totalCalories += docData.calories; // 收集文档id和所有字段,包含需要的mealTime mealList.push({ id: doc.id, ...docData }); console.log(doc.id, " -> ", docData); }); console.log("统计周期内总卡路里:", totalCalories); // 把结果return出去,作为async函数resolve的最终值 return { totalCalories, mealList }; } catch (error) { console.log("查询文档出错:", error); // 错误场景返回默认值,避免页面渲染崩溃 return { totalCalories: 0, mealList: [], error }; } }
第二步:在App.js中正确调用异步方法渲染
React中不能直接在组件函数顶层同步调用异步方法拿值,需要通过useEffect触发请求、useState存储结果触发视图更新,示例实现如下:
import { useState, useEffect } from 'react'; import { run } from './firebase.js'; function App() { // 用状态存储查询结果和加载状态 const [mealData, setMealData] = useState({ totalCalories: 0, mealList: [] }); const [loading, setLoading] = useState(true); useEffect(() => { const fetchData = async () => { setLoading(true); // 用await等待Promise决议,拿到最终返回结果 const res = await run(); setMealData(res); setLoading(false); }; fetchData(); // 空依赖数组表示组件挂载时仅执行一次查询 }, []); if (loading) return <div>数据加载中...</div>; return ( <div className="meal-stat"> <h2>近48小时饮食统计</h2> <p>*总摄入卡路里:{mealData.totalCalories}*</p> <ul className="meal-list"> {mealData.mealList.map(meal => ( <li key={meal.id}> <p>用餐时间:{new Date(meal.mealTime.toDate()).toLocaleString()}</p> <p>单餐卡路里:{meal.calories}</p> {/* 其余字段按需渲染即可 */} </li> ))} </ul> </div> ); } export default App;
注意事项
- 所有异步逻辑的结果必须通过
await或者.then()回调接收,不要尝试在同步代码中直接读取异步返回值的属性 - Firestore存储的时间字段读取后是Timestamp类型,渲染时需要调用
.toDate()转为原生Date对象再做格式化 - 异步请求请放在
useEffect回调中执行,不要直接写在组件函数顶层,否则每次组件重渲染都会重复发起请求
内容的提问来源于stack exchange,提问作者kkongkk
相关产品推荐
相关产品推荐

