简体   繁体   中英

How to compare Observables: NGRX and Working with Observables of Observables for efficient Angular 4 Rendering

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

  1. Taking our array of Observable - Whats the best way to compare the values of two Observables to see if any changed

example:
collection.distinctUntilChanged((a, b) => ?? compare values of ab ??)

  1. Working with Observables of Observables so that different child components can define the criteria of when a subscription is triggered makes other areas more difficult. Is there a better approach?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM