I have this Angular app in which I want to create a custom store for chat objects.
(codesandbox: https://codesandbox.io/s/chat-store-yw4yz)
Note: I know there are tools like ngrx but this is part of a bigger project and I would prefer to use this custom implementation so I understand better the behaviour of the store.
Context
The idea to make it quick and efficient is to have into the store two types of records:
- A dictionary (as a Map) with
key = chat id
andvalue = chat object
- An index (as a Map) for grouping chats by groupId, where
key = groupId
andvalue = chatId
So I have this interface definition:
export interface ChatStoreModel {
chats: Map<number, Chat>;
chatsByGroup: Map<number, number[]>;
}
When a chat object is saved into the Store two things should happen:
- chat object is saved into store
chats
- chat id is saved into store
chatsByGroup
in the corresponding groupId
Problem
It is not working to get the chat objects from a group. I do the following:
- get chat ids list from store
chatsByGroup
using groupId - for each chat id, get element from store
chats
Code:
getGroupChats$(groupId: number): Observable<Chat[]> {
return this._store.select("chatsByGroup").pipe(
map((chatsByGroup) => chatsByGroup.get(groupId) || []),
switchMap((ids) => {
if (!ids || !ids.length) {
return of([]);
}
const chatsList$: Observable<Chat>[] = ids.map((id) =>
this._store.select("chats").pipe(map((chats) => chats.get(id)))
);
return forkJoin([...chatsList$]).pipe(
map((list) => {
return ([] as Chat[]).concat(...list);
})
);
}),
shareReplay(1)
);
}
Debugging this code, it reaches the line with return forkJoin
and ids have the list of chats but it never reaches the line map((list) => {
so the app does not display the chat list.
Any help will be appreciated, thanks!
This is from the RxJS ForkJoin Doc. My guess is that the one of the Observable that you pass to the
forkJoin
completes without emitting anything, thusforkJoin
instantly completes without emitting anything.Try using
defaultIfEmpty
operator to be sure that this situation doesn't happen. I suggest doing something like so:This is again from the docs. This means that all of the Observables that you pass to the
forkJoin
have to complete in order for theforkJoin
to emit value itself.My guess is that
this._store.select("chats").pipe(map((chats) => chats.get(id))),
this line will run endlessly so you need to providetake(1)
for it to be completed, otherwiseforkJoin
won't emit anything. So try something like this:Lastly if you don't want to use
take(1)
, by which I mean you want to be subscribed to the Observable as long as it is emitting values, you might want to usecombineLatest
instead offorkJoin
. That's becauseforkJoin
waits for the completion to emit something, whilecombineLatest
will emit the latest values of the provided Observables.