Firebase RTDB触发变更事件时如何更新RTK Query缓存?
问题:结合RTK Query缓存与Firebase实时订阅自动更新数据
我正在使用Redux Toolkit、RTK Query(用于查询Firebase及其他API)和Firebase(处理认证与数据库)。现有代码能正常获取并缓存数据,但希望同时利用RTK Query的缓存功能和Firebase的事件订阅机制,让数据库发生变更(无论来自任何来源,甚至直接在Firebase控制台操作)时,自动更新RTK Query的缓存。我试过updateQueryCache和invalidateTags方法,但没找到理想方案,求指点方向。
现有代码
firebase.ts
export const onRead = ( collection: string, callback: (snapshort: DataSnapshot) => void, options: ListenOptions = { onlyOnce: false } ) => onValue(ref(db, collection), callback, options); export async function getCollection<T>( collection: string, onlyOnce: boolean = false ): Promise<T> { let timeout: NodeJS.Timeout; return new Promise<T>((resolve, reject) => { timeout = setTimeout(() => reject('Request timed out!'), ASYNC_TIMEOUT); onRead(collection, (snapshot) => resolve(snapshot.val()), { onlyOnce }); }).finally(() => clearTimeout(timeout)); }
awards.ts
const awards = dbApi .enhanceEndpoints({ addTagTypes: ['Themes'] }) .injectEndpoints({ endpoints: (builder) => ({ getThemes: builder.query<ThemeData[], void>({ async queryFn(arg, api) { try { const { auth } = api.getState() as RootState; const programme = auth.user?.unit.guidingProgramme!; const path = `/themes/${programme}`; const themes = await getCollection<ThemeData[]>(path, true); return { data: themes }; } catch (error) { return { error: error as FirebaseError }; } }, providesTags: ['Themes'], keepUnusedDataFor: 1000 * 60 }), getTheme: builder.query<ThemeData, string | undefined>({ async queryFn(slug, api) { try { const initiate = awards.endpoints.getThemes.initiate; const getThemes = api.dispatch(initiate()); const { data } = (await getThemes) as ApiResponse<ThemeData[]>; const name = slug ?.split('-') .map( (value) => value.substring(0, 1).toUpperCase() + value.substring(1).toLowerCase() ) .join(' '); return { data: data?.find((theme) => theme.name === name) }; } catch (error) { return { error: error as FirebaseError }; } }, keepUnusedDataFor: 0 }) }) });
解决方案
核心思路是在RTK Query的查询中启动Firebase实时订阅,当数据变化时直接更新RTK Query缓存,同时处理订阅的清理避免内存泄漏。
1. 修改Firebase工具函数,支持取消订阅
让onRead返回取消订阅的函数,方便后续清理:
// firebase.ts export const onRead = ( collection: string, callback: (snapshot: DataSnapshot) => void, options: ListenOptions = { onlyOnce: false } ): (() => void) => { const dbRef = ref(db, collection); const unsubscribe = onValue(dbRef, callback, options); return unsubscribe; };
2. 改造getThemes端点,添加实时订阅与缓存更新
在queryFn中先获取初始数据,再启动Firebase订阅,数据变化时用updateQueryData直接更新缓存,同时返回abort函数清理订阅:
getThemes: builder.query<ThemeData[], void>({ async queryFn(arg, api) { try { const { auth } = api.getState() as RootState; const programme = auth.user?.unit.guidingProgramme!; const path = `/themes/${programme}`; // 获取初始数据,返回给RTK Query初始化缓存 const initialThemes = await getCollection<ThemeData[]>(path, true); // 启动Firebase实时订阅 const unsubscribe = onRead(path, (snapshot) => { const updatedThemes = snapshot.val() as ThemeData[]; // 直接更新RTK Query缓存,无需重新请求 api.dispatch( awards.util.updateQueryData('getThemes', arg, (draft) => { // 根据数据结构选择更新方式,这里直接替换数组 draft.splice(0, draft.length, ...updatedThemes); }) ); }); // 返回初始数据+清理函数 return { data: initialThemes, abort: unsubscribe // 查询取消时自动调用,清理订阅 }; } catch (error) { return { error: error as FirebaseError }; } }, providesTags: ['Themes'], keepUnusedDataFor: 1000 * 60 }),
3. 优化getTheme端点,复用缓存数据
getTheme无需重新触发getThemes请求,直接读取缓存数据,并通过标签关联实现自动更新:
getTheme: builder.query<ThemeData | undefined, string | undefined>({ queryFn(slug, api) { try { // 从RTK Query缓存中直接获取getThemes的数据 const themesQuery = api.getState().api.queries['getThemes(undefined)']; const themes = themesQuery?.data as ThemeData[]; if (!themes || !slug) return { data: undefined }; // 转换slug为主题名称 const name = slug .split('-') .map(val => val.charAt(0).toUpperCase() + val.slice(1).toLowerCase()) .join(' '); return { data: themes.find(theme => theme.name === name) }; } catch (error) { return { error: error as FirebaseError }; } }, // 关联Themes标签,当getThemes缓存更新时自动重新计算结果 providesTags: (result) => result ? [{ type: 'Themes', id: result.name }] : ['Themes'], keepUnusedDataFor: 0 }),
关键说明
- 使用
updateQueryData直接修改缓存,比invalidateTags更高效,避免重复网络请求。 - 必须返回
abort函数,确保组件卸载或查询失效时取消Firebase订阅,防止内存泄漏。 getTheme通过标签关联,当getThemes缓存更新时会自动重新执行,保证数据一致性。
内容的提问来源于stack exchange,提问作者Mark Bailey
相关产品推荐
相关产品推荐

