Reputation: 428
I need to send a request with a large amount of data. Those data come from 6 different components - so basically it is very hard to do it without a reactive approach
. I decided to do something like ngrx component store
implementation (because of some reasons I've decided to implement it on my own).
I've created a store implementation that is being provided as a component provider, created a state which is being held in BehaviorSubject
, created a simple select
function which maps the state to needed data. In ngOnInit Parent Component
I made a request to the backend server to fetch foos
, setting a foo.isLoading
flag. Then I am subscribing and listened to foo.data
changes. If it does - I am calling backend server - I am setting boo.isLoading
and I am fetching boos
.
Foos
and Boos
are fetched correctly but there is an issue with boo.isLoading
flag - and I believe it comes from the order of calling state observers. Steps:
foo.isLoading is set to true
foo.data is set to backend response, foo.isLoading is set to false
then foo observer receives value and makes an HTTP request to get boos.
boo.isLoading is set to true and sends an HTTP request
boo.isLoading observers receives true
but since the state was changed twice - boo.isLoading observers haven't received the first value which now they do, so the most last received value is false
Any ideas on how to solve it? I've tried with switchMap
state to of(state)
so the previous subscription should be canceled when the new value "arrives" but it doesn't work.
Source code is here: https://stackblitz.com/edit/angular-ivy-qgbdxx
Upvotes: 0
Views: 315
Reputation: 2274
The problem is that you are using a read to trigger a write in this selector.
this.parentStore.select(state => state.foo.data).pipe(
filter(data => !!data),
tap(_ => this.parentStore.loadBooData())
).subscribe();
And since FooLoadCompletion
and BooLoadStart
processes are synchronous, the order of execution is messing with your flow.
I'll try to explain it.
The FooLoadComplete triggers the execution of the following selectors in this order, due to the moment they subscribe.
onFooLoadComplete
selector(foo.data).next(['a','b','c','d'])
selector(foo.isLoading).next(false)
selector(boo.isLoading).next(false)
But since your triggering the BooLoadStart
side effect in the foo.data selector the execution order ends up being like this.
onFooLoadComplete
selector(foo.data).next(yourData)
BooLoadStart
selector(foo.isLoading).next(false)
selector(boo.isLoading).next(true)
selector(foo.isLoading).next(false)
selector(boo.isLoading).next(false)
So the boo.isLoading
values get processed in the selector in the inverse order that you expect. And that is why the selector is printing false
.
To avoid this kind of problems you should keep your READ and WRITE pipelines independent.
In the code your supplying you could solve it removing the foo.data selector side effect and calling to loadBooData
in the setTimeout of loadFooData
loadFooData() {
this.state.next({
...this.state.value,
foo: {
...this.state.value.foo,
isLoading: true
}
});
setTimeout(() => {
this.state.next({
...this.state.value,
foo: {
...this.state.value.foo,
isLoading: false,
data: ['a', 'b', 'c', 'd']
}
})
this.loadBooData();
}, 2000)
}
Hope to have helped
cheers
Upvotes: 1