返回

Firestore 数据转换:Promise<DocumentData[]> 到数组的最佳实践

javascript

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 是处理异步操作和更新状态的常见模式。

  1. 声明状态: 使用 useState 声明一个状态变量,用于存储从 Firestore 获取的数据。 初始值可以设置为空数组 []

  2. 异步函数: 创建一个异步函数(例如 fetchDocuments),在其中调用 getMeds() 函数,并使用 await 等待 Promise 解析。

  3. 更新状态: 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 的情况。