Reputation: 14750
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:
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?
Upvotes: 14
Views: 5462
Reputation: 4287
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.
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
4. problems you can solve easier using rxjs
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
.
Upvotes: 9
Reputation: 1440
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.
Upvotes: 2
Reputation: 1631
There is no problem using BehaviorSubject.value
, you can use it whenever you need to. and in your case, yes definitely use value property for that, because you have encapsulated your change behavior into the method, so you can grab this value internally without any worries
toggle() {
this.myBoolSubject.next(!this.myBoolSubject.value)
}
Upvotes: 0
Reputation: 3604
I dont know your use case but normally, you should not use toggle()
without parameter, but set the value:
setValue(v: boolean) {
this.myBoolSubject.next(v);
}
The caller should always know what value to set. In a particuliar case where you really do not want to give the value, use .value
is fine.
Upvotes: -1