Reputation: 95
I have 3 selectors in index.ts file
export const selectDetails = createSelector(
// some code
);
export const selectTransactionResponse = createSelector(
// some code
);
export const selectAdditionalDetails = createSelector(
// some code
);
These selectors work in the respective components where they are used. I have a use case where I need all this information in a new component and for this I write the following selector.
export const selectSnapshotData = createSelector(
selectDetails,
selectTransactionResponse,
selectAdditionalDetails,
(Details, transactionResponse, additionalDetails) => ({
additionalDetails,
Details,
transactionResponse
} as SnapshotData)
);
Now when I use this selector in my new component as follows:
this.store.pipe(
select(fromDetails.selectSnapshotData),
filter(snapshotData => !!snapshotData),
takeUntil(this.destroy$)).subscribe(snapshotData => {
console.log("Let's see how many times this is logged");
console.log(snapshotData);
});
The code inside subscribe is executed 3 times, once each when each of the pieces of information is loaded on the store.
What I want is for this method to be executed once when all the information has been made available. For that I tried using forkjoin in the component rather than writing a custom selector in index.ts but that doesn't work either.
So is there a way to do this?
Upvotes: 2
Views: 4338
Reputation: 6411
Use combineLatest, this won't emit a value until all selectors emit a value.
combineLatest([
this.store.pipe(...select something, perhaps take(1) or distinctUntilChanged() to further restrict),
this.store.pipe(...another select from somewhere else),
]).pipe(
tap(([firstThing, secondThing]) => console.warn(firstThing, secondThing)),
).subscribe();
What you have to be a little careful of is how frequently each observable in combineLatest emits. If you are selecting from the same reducer across two selectors, one update to that reducer might result in two emits (after all have emitted a value to start with). To solve this you can use distinctUntilChanged or only take() however many you need.
Another handy thing is to pipe the selector to filter(x => x != null) so that the selected combined state only emits once all sub-selectors have a value.
Upvotes: 0
Reputation: 6424
First, Welcome to Stackoverflow.
Consider introducing an IF statement verifying that all the required data is available as demonstrated below:
export const selectSnapshotData = createSelector(
selectDetails,
selectTransactionResponse,
selectAdditionalDetails,
(Details, transactionResponse, additionalDetails) => {
if (Details && transactionResponse && additionalDetails) {
return {
additionalDetails,
Details,
transactionResponse,
} as SnapshotData;
}
}
);
Optionally using skip
operator:
this.store.pipe(
select(fromDetails.selectSnapshotData),
skip(2),
takeUntil(this.destroy$),
).subscribe(snapshotData => {
console.log("Let's see how many times this is logged");
console.log(snapshotData);
});
Upvotes: 3