如何从带Firebase Firestore实时监听器的函数持续获取更新值
问题核心
你当前的写法无法拿到持续的实时更新,根本原因是Firebase的onSnapshot是异步触发的回调函数:你在getConnectionRequestsFromDb中同步返回的data在函数执行瞬间还是空数组,后续Firebase推送的变更通知只会触发内部的回调逻辑,没有通道通知到外层调用方。
修改方案
1. 改造监听函数,支持回调通知
把数据处理和更新通知的逻辑放到onSnapshot回调内部,通过传入的回调参数把最新数据、错误抛出给调用方,同时返回取消监听的方法:
// LISTENER FN 改造后 export const getConnectionRequestsFromDb = (onUpdate, onError) => { const uid = getUID() const q = query( collection(database, "inbox"), where("uids", "array-contains-any", [uid]), where("type", "==", "connectionRequest"), limit(50) ) const unsubscribe = onSnapshot(q, (querySnapshot) => { // 注意:每次快照更新要重置数组,不要累加,否则数据会重复 const data = [] querySnapshot.forEach((doc) => data.push(doc.data())) const formattedData = convertDatesIntoJsTimestamps(data) // 把最新数据传给调用方的回调 onUpdate(formattedData) }, (err) => { // 错误捕获 onError?.(err) }) // 直接返回取消监听方法即可,不需要同步返回空数据 return unsubscribe }
2. 改造调用函数,在回调中触发dispatch
在Redux Thunk中把更新dispatch的逻辑放到回调里,每次Firebase推送更新就自动触发状态更新,同时把unsubscribe方法返回给上层,方便组件卸载时取消监听避免内存泄漏:
// INVOKING FN 改造后 export const getConnectionRequests = () => { return async (dispatch) => { dispatch({ type: "CONNECTIONS_ACTIONS/GET_CONNECTIONS_REQUEST/pending" }) // 传入回调,每次有实时更新就自动执行 const unsubscribe = getConnectionRequestsFromDb( (data) => { dispatch({ type: "CONNECTIONS_ACTIONS/GET_CONNECTIONS_REQUEST/fulfilled", payload: data, }) }, (error) => { dispatch({ type: "CONNECTIONS_ACTIONS/GET_CONNECTIONS_REQUEST/rejected", payload: error }) } ) // 返回取消监听方法,上层组件卸载时调用即可停止监听 return unsubscribe } }
额外注意事项
- 组件中调用这个thunk的时候,要把返回的unsubscribe方法存下来,在
useEffect的清理函数/类组件的componentWillUnmount生命周期中执行,否则会出现内存泄漏。 - 如果需要处理增量更新(比如只新增/修改/删除变更的条目,不用全量替换数组),可以在
onSnapshot回调中遍历querySnapshot.docChanges()判断变更类型处理,性能更高。
内容的提问来源于stack exchange,提问作者Walter Monecke
相关产品推荐
相关产品推荐

