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

React.js读取Firebase Firestore数组字段报错页面无法渲染如何解决

问题描述

我在React项目中尝试从Cloud Firestore数据库读取todo数组字段,通过console.log打印时可看到空数组标识,但同时会抛出错误,导致页面无法正常渲染。
控制台打印输出截图


相关代码

Firestore.js(Firebase数据读取逻辑)

export const userTodoList = async (email) => {
  await onSnapshot(doc(db, "users", email), (doc) => {
    console.log(doc.data().todos);
    return doc.data().todos;
  });
};

MainPage.js(数据获取与组件传值逻辑)

const MainPage = () => {
  const todoArray = userTodoList(auth.currentUser.email);
  const [todos, setTodos] = useState(todoArray);
  return (
    <div>
      <section>
        <NavBar></NavBar>
        <ToDoForm />
      </section>
      <section>
        <CardList array={todos} />
      </section>
    </div>
  );
};

CardList.js(列表渲染组件)

我已经添加了数组长度为0的判断逻辑,但仍然报错:

const CardList = (props) => {
  const array = props.array;
  if (array.length === 0) {
    return (
      <div className="grid_container">
        <h2>Found no todos</h2>
      </div>
    );
  }
  return (
    <div className="grid_container">
      {array.map((todo) => (
        <Card title={todo.title} />
      ))}
    </div>
  );
};

报错截图

报错截图1
报错截图2


问题原因&修复方案

你的代码有3个核心问题,逐个改完就能正常运行:

  • onSnapshot的返回值逻辑完全错误
    onSnapshot是事件监听类方法,没有办法通过回调里return把数据传给外层,你写的async/await也等不到回调里的返回值,userTodoList函数实际返回的是undefined,根本拿不到todos数组。
  • 初始状态没有兜底值,首屏渲染直接报错
    你把调用userTodoList的返回值(也就是undefined)直接塞给useState当初始值,首次渲染的时候todos是undefined,传到CardList里访问array.length直接触发Cannot read properties of undefined错误,这就是你看到的报错来源。
  • 没有在React的生命周期里绑定/解绑Firestore监听
    直接在组件函数体里调用监听方法,会导致组件每次重渲染都重复绑定监听,造成内存泄漏和重复触发。

之前写的userTodoList封装方法因为逻辑不符合onSnapshot的调用规则,可以直接移除,不需要再保留。


修复后的代码

1. 修改MainPage.js的数据获取逻辑

import { useEffect, useState } from 'react';
import { doc, onSnapshot } from 'firebase/firestore';
import { db, auth } from '你的firebase配置文件路径';

const MainPage = () => {
  // 初始值给空数组,首屏渲染不会出现undefined的问题
  const [todos, setTodos] = useState([]);

  useEffect(() => {
    // 先判断当前用户是否存在,避免auth还没初始化就拿email报错
    if (!auth.currentUser?.email) return;
    // 绑定快照监听,拿到unsubscribe方法用于组件卸载时解绑
    const unsubscribe = onSnapshot(
      doc(db, "users", auth.currentUser.email),
      (docSnap) => {
        // 取数据的时候做兜底,文档不存在的话默认空数组
        const data = docSnap.data();
        setTodos(data?.todos || []);
      }
    );
    // 组件卸载时取消监听,避免内存泄漏
    return () => unsubscribe();
  }, []);

  return (
    <div>
      <section>
        <NavBar></NavBar>
        <ToDoForm />
      </section>
      <section>
        <CardList array={todos} />
      </section>
    </div>
  );
};

2. 给CardList加边界兜底

const CardList = (props) => {
  // 兼容array未传值/不是数组的边界情况
  const array = props.array || [];
  if (array.length === 0) {
    return (
      <div className="grid_container">
        <h2>Found no todos</h2>
      </div>
    );
  }
  return (
    <div className="grid_container">
      {array.map((todo, index) => (
        // 列表渲染记得加唯一key,避免控制台警告
        <Card key={todo.id || index} title={todo.title} />
      ))}
    </div>
  );
};

改完之后首屏渲染时todos是空数组,CardList正常显示无待办的提示,等Firestore快照返回数据后会自动更新状态,渲染列表内容,不会再触发报错。


内容的提问来源于stack exchange,提问作者GokaySendil

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:45:06