Firestore 数据转换:Promise<DocumentData[]> 到数组的最佳实践
2024-11-19 09:06:25
Firestore 数据转换:从 Promise<DocumentData[] | undefined> 到数组
从 Firestore 获取数据时,经常会遇到 Promise<DocumentData[] | undefined>
类型的返回值。这表示返回的是一个 Promise,它最终会解析为一个 DocumentData
数组或 undefined
。 如何将这种类型转换为可以直接使用的数组,是许多开发者面临的常见问题。 本文将探讨几种有效的解决方案,并分析其优缺点,帮助你选择最适合你的方法。
理解问题根源
Promise
代表异步操作的结果。 你不能直接访问 Promise 中的值,必须等待它解析。 .map()
方法用于数组,而不能直接用于 Promise。 TypeScript 的类型检查机制会阻止你在 Promise 上调用 .map()
,从而引发错误。
解决方案一: async/await 配合 useState
在 React 组件中,使用 useState
结合 async/await
是处理异步操作和更新状态的常见模式。
-
声明状态: 使用
useState
声明一个状态变量,用于存储从 Firestore 获取的数据。 初始值可以设置为空数组[]
。 -
异步函数: 创建一个异步函数(例如
fetchDocuments
),在其中调用getMeds()
函数,并使用await
等待 Promise 解析。 -
更新状态: Promise 解析后,使用
setMeds
更新状态变量。 使用可选链操作符?.
和空值合并运算符??
可以安全地处理undefined
值。
import { useState, useEffect } from 'react';
import { getMeds } from './firebase'; // 导入你的 getMeds 函数
import { DocumentData } from 'firebase/firestore';
const MyComponent: React.FC = () => {
const [meds, setMeds] = useState<DocumentData[] | undefined>([]);
useEffect(() => {
const fetchDocuments = async () => {
try {
const docs = await getMeds();
setMeds(docs ?? []);
} catch (error) {
console.error("获取数据失败:", error);
}
};
fetchDocuments(); // 调用异步函数
}, []);
return (
<div>
{meds?.map((med, index) => ( // 使用可选链操作符 ?. 安全地调用 map
<div key={index}>{/* 渲染你的组件 */}</div>
))}
</div>
);
};
export default MyComponent;
解决方案二: then 方法
then
方法是处理 Promise 的另一种方式。它接受一个回调函数,当 Promise 解析完成后执行。
import { useState, useEffect } from 'react';
import { getMeds } from './firebase';
import { DocumentData } from 'firebase/firestore';
const MyComponent: React.FC = () => {
const [meds, setMeds] = useState<DocumentData[] | undefined>([]);
useEffect(() => {
getMeds().then(docs => {
setMeds(docs ?? []);
}).catch(error => {
console.error("获取数据失败:", error);
});
}, []);
// ... 后续代码与方案一相同
};
export default MyComponent;
类型安全提示
为了提高代码的可读性和安全性,建议显式声明 meds
状态的类型为 DocumentData[] | undefined
, 并在渲染时使用可选链操作符 ?.
进行安全访问, 避免出现 undefined
导致的运行时错误。
getMeds 函数的改进建议
为了避免在 getMeds
函数中处理 undefined
, 可以修改函数使其始终返回一个数组,即使集合为空。
export const getMeds = async (): Promise<DocumentData[]> => {
try {
const user = auth.currentUser;
if (user) {
const snapshot = await getDocs(collection(db, "Users", user.uid, "Medications"));
return snapshot.docs.map(doc => doc.data());
}
return []; // 如果用户未登录或集合为空,返回空数组
} catch (err: any) {
console.error("获取数据失败:", err);
return []; // 出错时也返回空数组
}
};
这样,你就可以简化 useState
和后续的处理逻辑,无需再处理 undefined
的情况。