ReactJS从Firebase/Firestore渲染用户数据异常问题排查
React + Firestore 数据渲染异常修复方案
问题现象
- 初始加载或修改代码后,表格显示所有用户的全部文档数据,仅刷新页面后才展示当前登录用户的数据
- 添加新值时,表格显示全部数据且当前用户数据重复,刷新后恢复正常
核心问题
- 错误监听了整个
data集合,而非当前用户的单个文档,导致拉取所有用户数据 - 快照回调中未重置数据数组,新增数据时重复累加旧内容
- 未清理
onAuthStateChanged和onSnapshot的监听,引发重复订阅问题
修复后的完整代码
export default function Nutrition() { const [data, setData] = useState([]); useEffect(() => { let unsubscribeAuth; let unsubscribeSnapshot; unsubscribeAuth = onAuthStateChanged(auth, async (user) => { if (user) { const uid = user.uid; const userDocRef = doc(db, "data", uid); // 校验用户文档是否存在 const initialSnapshot = await getDoc(userDocRef); if (initialSnapshot.exists()) { // 仅监听当前用户的文档变化,而非整个集合 unsubscribeSnapshot = onSnapshot(userDocRef, (docSnapshot) => { const { value, quantity } = docSnapshot.data(); // 每次快照触发时重置数组,避免重复累加 const foods = []; // 处理空数组情况,防止报错 const dataLength = value?.length || 0; for (let i = 0; i < dataLength; i++) { foods.push({ value: value[i], quantity: quantity[i], id: `${uid}-${i}` // 生成唯一key,避免React渲染警告 }); } setData(foods); }); } else { console.log("用户文档不存在"); setData([]); } } else { console.log("用户未登录"); setData([]); // 用户登出时清理快照监听 if (unsubscribeSnapshot) unsubscribeSnapshot(); } }); // 组件卸载时清理所有监听,防止内存泄漏 return () => { if (unsubscribeAuth) unsubscribeAuth(); if (unsubscribeSnapshot) unsubscribeSnapshot(); }; }, []); return ( <React.Fragment> <div className='container mt-3'> <table className='table table-hover'> <thead> <tr> <th>#</th> <th>食物</th> <th>数量</th> </tr> </thead> <tbody> {data.map((row, index) => ( <tr key={row.id}> <td>{index + 1}</td> <td>{row.value}</td> <td>{row.quantity}</td> </tr> ))} </tbody> </table> <button className='btn btn-dark mt-3'>重置表格</button> </div> </React.Fragment> ); }
关键修复说明
- 精准数据监听:将
onSnapshot的监听目标从整个data集合改为当前用户的单个文档userDocRef,确保只获取当前用户的私有数据 - 数据重置机制:每次快照回调时重新初始化
foods数组,彻底解决重复累加问题 - 监听生命周期管理:在组件卸载和用户登出时清理所有订阅,避免内存泄漏和重复触发
- 唯一key优化:为每个表格行项生成唯一
id,消除React列表渲染的key警告
内容的提问来源于stack exchange,提问作者Ashka
相关产品推荐
相关产品推荐

