Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RxJS Observable: Emit when specific part of the object changes

Tags:

rxjs

rxjs5

I have a complex object having child objects and arrays and so on.

I can easily create a Subject/BehaviourSubject or Observable from this object so that I can "next" (emit) the new state to the subscribers.

[ex: let appEnv$ = new Rx.BehaviorSubject<IWebSocketAppEnv>(appEnv);]

However I don't want all my subscribers to be notified every time the object changes. For instance for one of my subscriber, I only want to be notified when a change occurs on an elements of an array of that object.

Actually what I want is something redux is already doing. In redux, I can subscribe to the store but selecting only a child element.

I want to implement the same infrastructure for my back-end websocket server application.

How can I achieve that with RxJS?

like image 223
Mustafa Ekim Avatar asked Jul 04 '17 08:07

Mustafa Ekim


1 Answers

Use distinctUntilChanged operator. (docs)

Let's assume that the object's structure is:

{
  articleId: 0,
  comments: [ 'lorem', 'ipsum' ]
}

We want to be as cool as firebase guys so we'll update comments in real-time when the updated object has a different array.

I'm using RxJS5 for observables and lodash to compare arrays because operator's default behavior won't compare them in the way we want them to be compared.

// Our BehaviorSubject which emits new object.
// For those who don't know: it'll emit the latest emitted value when subscribing to it.
const object$ = new Rx.BehaviorSubject({
  articleId: 0,
  comments: [],
}); 

// If you want specific parts of your application 
// to react only when a specific part of the object has changed, you 
// have to create another observable using 'map' and 
// 'distinctUntilChanged' operator and subscribe to it.
const comments$ = object$
  .map(object => object.comments) // we want to emit comments only
  .distinctUntilChanged((a, b) => _.isEqual(a, b)); // emit only when currently emitted value is different than previous one.

comments$.subscribe(v => console.log(v)); // log fresh comments

object$.next({
  articleId: 1,
  comments: [],
});

object$.next({
  articleId: 1,
  comments: ['lorem', 'ipsum'],
});

object$.next({
  articleId: 2,
  comments: ['lorem', 'ipsum'],
});

object$.next({
  articleId: 2,
  comments: ['lorem', 'ipsum', 'dolor', 'sit', 'amet'],
});

effect:

[]
["lorem", "ipsum"]
["lorem", "ipsum", "dolor", "sit", "amet"]

effect without distinctUntilChanged operator:

[]
[]
["lorem", "ipsum"]
["lorem", "ipsum"]
["lorem", "ipsum", "dolor", "sit", "amet"]

JSFiddle

like image 186
Michał Pietraszko Avatar answered Oct 22 '22 19:10

Michał Pietraszko