如何在RTK查询中取消订阅Firebase onSnapShot ?



我正在React Native中创建一个依赖于实时更新的应用程序。当用户在会话中更新数据时,另一个用户需要立即看到该更新而无需刷新他们的应用程序。我使用RTK查询来管理我的商店,但无法弄清楚如何关闭内存泄漏,如果我在查询中使用onSnapShot。我是否需要考虑另一个Redux解决方案?

我试过通过道具传递数据来管理数据,但是当使用复杂的组件时,管理变得有点复杂。

我从组件中的以下内容开始,但希望将其移动到api中:


export const teamPlayersApi = createApi({
reducerPath: "teamPlayers",
baseQuery: fakeBaseQuery(),
tagTypes: ["TeamPlayer"],
endpoints: (builder) => ({
fetchTeamPlayersByMatchId: builder.query({
async queryFn(matchId) {
let players = [];
return new Promise((resolve, reject) => {
const playersRef = query(
collection(db, "teamPlayers"),
where("playerMatchId", "==", matchId)
);
//real time update
onSnapshot(playersRef, (snapshot) => {
players = snapshot.docs.map((doc) => ({
id: doc.id,
player: doc.data()
}));
resolve({ data: players });
});
});
}
})
})
})

更新:@phry下面的代码工作,但我没有看到一个重新渲染发生时,'草案'的改变。什么触发了重新渲染?

const {
data: fetchPlayersData,
error: fetchPlayersError,
isFetching: isFetchingPlayers
} = useFetchTeamPlayersStreambyMatchIdQuery(matchId);
useEffect(() => {
console.log("players changed");
}, [fetchPlayersData]);

…AND在API

fetchTeamPlayersStreambyMatchId: builder.query({
async queryFn(matchId) {
try {
const teamPlayersRef = collection(db, "teamPlayers");
const tpq = query(
teamPlayersRef,
where("playerMatchId", "==", matchId)
);
const querySnap = await getDocs(tpq);
let players = [];
querySnap.forEach((doc) => {
return players.push({
id: doc.id,
player: doc.data()
});
});
return { data: players };
} catch (error) {
console.error(error.message);
return { error: error.message };
}
},
async onCacheEntryAdded(
matchId,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
let unsubscribe = () => {};
try {
await cacheDataLoaded;
const playersRef = query(
collection(db, "teamPlayers"),
where("playerMatchId", "==", matchId)
);
unsubscribe = onSnapshot(playersRef, (snapshot) => {
players = snapshot.docs.map((doc) => ({
id: doc.id,
player: doc.data()
}));
updateCachedData((draft) => {
draft = [];
draft.push(players);
});
});
} catch {}
await cacheEntryRemoved;
unsubscribe();
}
})

这有两个不同的部分:

  • aqueryFn通过onValue获取初始数据。这是您的查询进入loading状态并在某个点以第一个值结束的点。
  • onCacheEntryAdded生命周期函数,调用onSnapshot,更新值并保持订阅。此时将生成订阅,并在组件使用缓存条目时使用新值更新数据。
export const teamPlayersApi = createApi({
reducerPath: "teamPlayers",
baseQuery: fakeBaseQuery(),
tagTypes: ["TeamPlayer"],
endpoints: (builder) => ({
fetchTeamPlayersByMatchId: builder.query({
async queryFn(matchId) {
let players = [];
return {
data: await new Promise((resolve, reject) => {
const playersRef = query(
collection(db, "teamPlayers"),
where("playerMatchId", "==", matchId)
);
// probably more logic here to get your final shape
onValue(
playersRef,
(snapshot) => resolve(snapshot.toJSON()),
reject
);
}),
};
},
async onCacheEntryAdded(
matchId,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
let unsubscribe = () => {};
try {
await cacheDataLoaded;
const playersRef = query(
collection(db, "teamPlayers"),
where("playerMatchId", "==", matchId)
);
unsubscribe = onSnapshot(playersRef, (snapshot) => {
players = snapshot.docs.map((doc) => ({
id: doc.id,
player: doc.data(),
}));
updateCachedData((draft) => {
// or whatever you want to do
draft.push(players);
});
});
} catch {}
await cacheEntryRemoved;
unsubscribe();
},
}),
}),
});

感谢Oren在https://orizens.com/blog/integration-of-firebase-firestore-with-redux-toolkit-query/

下面的工作解
fetchTeamPlayersStreambyMatchId: builder.query({
async queryFn(matchId) {
return { data: null };
},
async onCacheEntryAdded(
matchId,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
let unsubscribe = () => {};
try {
await cacheDataLoaded;
const playersRef = query(
collection(db, "teamPlayers"),
where("playerMatchId", "==", matchId)
);
unsubscribe = onSnapshot(playersRef, (snapshot) => {
updateCachedData((draft) => {
return snapshot?.docs?.map((doc) => ({
id: doc.id,
player: doc.data()
}));
});
});
} catch (error) {
console.log("error in players!", error);
throw new Error("Somehting went wrong with players");
}
await cacheEntryRemoved;
unsubscribe();
},
providesTags: ["TeamPlayer"]
})