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 idandvalue = chat object - An index (as a Map) for grouping chats by groupId, where
key = groupIdandvalue = 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
chatsByGroupin 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
chatsByGroupusing 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
forkJoincompletes without emitting anything, thusforkJoininstantly completes without emitting anything.Try using
defaultIfEmptyoperator 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
forkJoinhave to complete in order for theforkJointo 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, otherwiseforkJoinwon'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 usecombineLatestinstead offorkJoin. That's becauseforkJoinwaits for the completion to emit something, whilecombineLatestwill emit the latest values of the provided Observables.