Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RXJS - BehaviorSubject: proper use of .value

Tags:

angular

rxjs

I'm wondering if there is actually a proper use for behaviorSubject.value. According to to this answer, we should ONLY get values via subscribing.

A case where it seems okay to me is where I'm using the .value in order to determine the next value to push through the stream, like when I'm toggling a simple boolean:

myBoolSubject = new BehaviorSubject(false);

toggle() {
  this.myBoolSubject.next(!this.myBoolSubject.value);
}

The alternative using subscribe() would look like:

toggle() {
   this.myBoolSubject.pipe(take(1)).subscribe(
      val => this.myBoolSubject.next(!val)
   );
}

From looking at the rxjs source and the aforementioned answer, the difference between these two approaches are that .value will throw when:

  • the subject has been completed
  • there has been an error

In this simple case, I'm not going to complete the subject and I don't think errors are a concern since I'm just pushing simple boolean values through this subject.

Is this a valid use case for behaviorSubject.value? Are there others?

Another case where it seems okay to use .value is when constructing a new object from the previously emitted value:

private state = new BehaviorSubject<State>(INITIAL_STATE);
public state$ = this.state.asObservable();

public updateState(changes: Partial<State>){
    const newState = {...this.state.value, ...changes};
    this.state.next(newState);
}

The alternative would be to cache the latest state emission in another variable, something like this:

private _state = INITIAL_STATE;
private state = new BehaviorSubject<State>(INITIAL_STATE);
public state$ = this.state.asObservable();

public updateState(changes: Partial<State>){
    const newState = {...this._state, ...changes};
    this.state.next(this._state = newState);
}

Are there any concerns I'm overlooking?

like image 935
BizzyBob Avatar asked Jun 08 '20 12:06

BizzyBob


People also ask

How do I use BehaviorSubject in RxJS?

1. First create a BehaviorSubject in order service which holds the initial state of order count ,so that it can be used by any component. 2. Now all observers(3 components) need to subscribe to source observable to get current value and show it on UI.

How do I get the latest value from BehaviorSubject?

The BehaviorSubject There are two ways to get this last emited value. You can either get the value by accessing the . value property on the BehaviorSubject or you can subscribe to it. If you subscribe to it, the BehaviorSubject will directly emit the current value to the subscriber.

What is the correct answer about ReplaySubject and BehaviorSubject in RxJS?

If you want to provide an initial value at subscription time, even if nothing has been pushed to a Subject so far, use the BehaviorSubject. If you want to have the last value replayed to an observer, even if a Subject is already closed, use the ReplaySubject(1).

When should I use ReplaySubject?

Use a ReplaySubject when you need more than the last given value. (For example, the previous five values) Or you want to set a time window for the values can be validly sent to subscribers. Use an AsyncSubject when you only want the last value to be passed to the subscribers.


2 Answers

1. boolean example without subscribe and usage of .value

const { Subject } = rxjs;
const { scan, startWith } = rxjs.operators;

myToggle$$ = new Subject();

myBool$ = myToggle$$.pipe(
  scan((acc) => !acc, false),
  startWith(false)
)

myBool$.subscribe(v => console.log('result: ', v));

myToggle$$.next();
myToggle$$.next();
myToggle$$.next();
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>

2. reactive programming

Avoid intricate stateful programs, using clean input/output functions over observable streams.

reactivex.io

You are already using rxjs where you could also say, you want to use straight imperative code:

myBool = false;
function myToggle() { myBool = !myBool }

console.log('result: ', myBool);
myToggle();
console.log('result: ', myBool);
myToggle();
console.log('result: ', myBool);
myToggle();
console.log('result: ', myBool);

The .value does one important thing to your code that diffs from a clean reactive solution. The business logic is moved outside the stream into the toggle function. As the official docs mention the goal of rxjs is to have clean inputs/outsputs. That means that every input you are giving your stream receives always the same output. That means your transformation should be handled inside the stream, without any side-effects.

3. downsides of side-effects

  • harder testing (mocking)
  • hard to comprehend effects
  • hard to expand functionality (code is built upon actions and not focused on effects)

4. problems you can solve easier using rxjs

  • async / threaded data over time (cancelation, pause, ..)
  • operators
  • easy composition of different streams (helps you focus on what should happen and less when)
  • concurrency (easy split up and merge of streams)

5. my own opinion

Reactive programming handles several problems for you, or at least makes it easier. In you shown example I cannot see why you need to use reactive programming. It would be also completely fine if you avoid rxjs and programm toggle() fully imperative. But the moment you dedided going with a BehaviorSubject you decided going reactive programming.

6. summary

You can implement your problem by using .value but you should not.

like image 80
Jonathan Stellwag Avatar answered Sep 18 '22 09:09

Jonathan Stellwag


I'd suggest you avoid directly managing state in your code. It beats the purpose of using Rx.

In your example, the "toggle" is called when a particular event happens, right? You just need to track that particular event by an obsearvable, then use "scan" to transform the stream of such events into a stream of state.

Here is an example, I use an array of number to simulate your toggle event and assume the initial state is "true":

Rx.Observable.from([1,1,1,1])
  .pipe(
     scan((acc,_) => !acc, false)
   ).subscribe(st => console.log(st))  

The output will be - true, false, true, false

The key here is to think everything as stream of events. With the help of Rx lib, you can easily transform and fabricate these streams to implement your logic.

like image 21
ch_g Avatar answered Sep 20 '22 09:09

ch_g