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> ); };
报错截图


问题原因&修复方案
你的代码有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
相关产品推荐
相关产品推荐

