React JS 如何存储 Firestore 集合实时监听器返回的数据
问题根源
你当前的写法存在两个核心问题:
onSnapshot是异步回调,你在模块顶层同步打印fetchedTickets的时候,Firestore 的请求还未返回,回调根本没有执行,所以拿到的只能是空数组- 普通 JS 数组的修改不会触发 React 组件重渲染,也无法主动通知其他依赖该数据的模块更新,就算后续数组被填充,其他使用方也感知不到数据变化
解决方案
针对 React 项目场景,推荐两种常用的实现方式:
方案1:自定义Hook + 全局Context(最贴合React生态,适合跨组件调用)
既能拿到实时更新的 tickets 数据,又能全局共享,自动触发依赖组件的重渲染。
首先创建上下文文件:
// TicketsContext.jsx import { createContext, useContext, useEffect, useState } from 'react'; import firebase from '你的firebase初始化文件路径'; const TicketsContext = createContext(); export function TicketsProvider({ children }) { const [fetchedTickets, setFetchedTickets] = useState([]); const firestore = firebase.firestore(); useEffect(() => { const unsubscriber = firestore.collection("tickets").onSnapshot((snapshot) => { setFetchedTickets(prev => { const updatedTickets = [...prev]; snapshot.docChanges().forEach((change) => { // 建议携带文档id,方便后续修改、删除操作 const ticketData = { id: change.doc.id, ...change.doc.data() }; if (change.type === "added") { updatedTickets.push(ticketData); } if (change.type === "modified") { const index = updatedTickets.findIndex(t => t.id === ticketData.id); if (index !== -1) updatedTickets[index] = ticketData; } if (change.type === "removed") { return updatedTickets.filter(t => t.id !== ticketData.id); } }); return updatedTickets; }); }); // 组件卸载时取消监听,避免内存泄漏 return () => unsubscriber(); }, []); return ( <TicketsContext.Provider value={fetchedTickets}> {children} </TicketsContext.Provider> ); } // 封装调用Hook,简化使用 export function useTickets() { return useContext(TicketsContext); }
在项目根组件中注入Provider:
// App.jsx / main.jsx import { TicketsProvider } from './TicketsContext'; function App() { return ( <TicketsProvider> {/* 你的所有业务组件 */} </TicketsProvider> ); }
任意组件需要使用数据时直接调用Hook即可:
// 业务组件示例 import { useTickets } from './TicketsContext'; function TicketList() { const tickets = useTickets(); return <div> {tickets.map(ticket => <div key={ticket.id}>{ticket.title}</div>)} </div> }
方案2:发布订阅模式(适合非React组件的JS文件共享数据)
如果你有工具函数等非React模块也需要用到tickets数据,可以用简单的订阅器实现:
// ticketStore.js import firebase from '你的firebase初始化文件路径'; const firestore = firebase.firestore(); let fetchedTickets = []; const subscribers = []; // 通知所有订阅方数据更新 const notifySubscribers = () => { subscribers.forEach(cb => cb([...fetchedTickets])); }; // 启动实时监听 const unsubscriber = firestore.collection("tickets").onSnapshot((snapshot) => { snapshot.docChanges().forEach((change) => { const ticketData = { id: change.doc.id, ...change.doc.data() }; if (change.type === "added") { fetchedTickets.push(ticketData); } if (change.type === "modified") { const index = fetchedTickets.findIndex(t => t.id === ticketData.id); if (index !== -1) fetchedTickets[index] = ticketData; } if (change.type === "removed") { fetchedTickets = fetchedTickets.filter(t => t.id !== ticketData.id); } }); notifySubscribers(); }); // 对外暴露订阅方法 export function subscribeTickets(callback) { subscribers.push(callback); // 首次订阅直接返回当前最新数据 callback([...fetchedTickets]); // 返回取消订阅方法 return () => { const index = subscribers.indexOf(callback); if (index !== -1) subscribers.splice(index, 1); }; } // 对外暴露同步获取当前最新数据的方法 export function getTicketsSnapshot() { return [...fetchedTickets]; } // 全局销毁监听方法,项目卸载时可调用 export function destroyTicketListener() { unsubscriber(); }
其他文件使用示例:
import { getTicketsSnapshot, subscribeTickets } from './ticketStore'; // 同步获取当前最新数据 const currentTickets = getTicketsSnapshot(); // 监听数据变化 const unsub = subscribeTickets((latestTickets) => { // 此处写你的业务逻辑 console.log('tickets已更新', latestTickets); }); // 不需要监听时调用取消,避免内存泄漏 // unsub();
内容的提问来源于stack exchange,提问作者Pavel
相关产品推荐
相关产品推荐

