OVERVIEW
Were following the container -> component pattern and using ngrx for centralizing our applications state.
The app structure is something such as:
Container
Child_Comp_1
Child_Comp_2
Child_Comp_3
Originally, there was a single observable of the data subscribed to at the Container, then the relative data passed as inputs to child components. such as
Observable<
{
data,
collection: []
}>
However, any update triggered the ngrx selector and caused a AngularJS re-rendering down the chain of components.
We moved to serving up observables of observables,
Observable<
{
data: Observable<data>
collection: Observable<Observable<Item>[]>
}>
We can now use RxJs functions such as DistinctUntilChanged(predicate) to specify when a subscription in a component is actually triggered.
And use ChangeDetection.markForCheck() which will limit where the re-rendering occurs
PROBLEM & QUESTION
There are two problems
example:
collection.distinctUntilChanged((a, b) => ?? compare values of ab ??)
Example:
Child Comp
collection
.distinctUntilChanged((a, b) => a.length === b.length)
.subscribe(items => ....) // only fired if length changed
The way I would do this is to just send a value through and derive observables from it rather than sending observables of observables.
private subj: Subject<{data:any, collection: any[]}> = new Subject<{data:any, collection: any[]}>();
src$: Observable<{data:any, collection: any[]}> = this.subj.distinctUntilChanged();
data$: Observable<any> = this.src$.pluck('data').distinctUntilChanged();
collection$: Observable<any[]> = this.src$.pluck('collection').distinctUntilChanged();
this.src$.subscribe(v => console.log(v));
this.data$.subscribe(v => console.log(v));
this.collection$.subscribe(v => console.log(v));
the default behavior of distinctUntilChanged is an object reference (or primitive) comparison. So the key is to enforce object immutability and only alter objects that need updates and to rereference them when you do.
const obj = { data: {}, collection: []; };
this.subj.next(obj); // all subscribers trigger
const dataUpdate = Object.assign({}, obj, {data: { prop1:'new Val' }}); // change only the data property, collection untouched
this.subj.next(dataUpdate); //src and data trigger
const collUpdate = Object.assign({}, dataUpdate, { collection: [1] }); // change only collection property, data untouched
this.subj.next(collUpdate); //src and coll fire
const collUpdate2 = Object.assign({}, collUpdate, { collection: collUpdate.collection.concat([2]) }); // add to existing collection
this.subj.next(collUpdate2); //src and coll fire
const collUpdate3 = Object.assign({}, collUpdate2, { collection: collUpdate2.collection.splice(0,1) }); //reomve from existing collection
this.subj.next(collUpdate3); //src and coll fire
this is the general pattern, take your last value, and your update, and apply the update in such a way that it only updates the pieces that should be updated, including all of it's ancestors. This is what ngrx really does under the hood, it uses the scan operator, which takes your accumulated vallue (last value) and your new action (your update) and applies the change in such a way that only pieces that need updating get updated (the reducer). ngrx's "select" function also takes care of all the distinctUntilChanged() stuff so you don't need to worry about it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.