tldr;
Most Angular applications need some amount of state management. In some applications, a state management library like NgRX or Akita might make sense. But many times storing application state in RxJS services will work perfectly. We’ll go over how to do this using RxJS BehaviorSubjects
and Observables
.
BehaviorSubjects
Before jumping in to examples, let’s talk about BehaviorSubjects
. BehaviorSubjects
are a variant of RxJS Subjects
. Components can subscribe to a BehaviorSubject
and get updates when something changes. They do need to be primed with an initial value, but that value can be null
if desired. There is one difference, though, between Subjects
and BehaviorSubjects
. If you subscribe to a Subject
, you will only receive values that are emitted after subscribing. But if you subscribe to a BehaviorSubject
, you will get the last emitted value and all future emitted values. Subjects
are great for many situations, but when managing state in an application I’ve found that BehaviorSubjects
are better suited.
Managing State
Okay, now that we are familiar with BehaviorSubjects
, let’s look at an example of how to leverage those to manage state in an application.
I was recently working on an application that used information about the logged in person (a Member
) throughout the app. The information didn’t update frequently, so I was able to get the information once and store it in a BehaviorSubject
so that the app could store that information and give it out as needed. If it did update, I could push the new value into the BehaviorSubject
and all subscribers were updated automatically. Here’s some skeleton code:
export class MyService {
private memberBS: BehaviorSubject<Member> = new BehaviorSubject<Member>(null);
public member$: Observable<Member> = this.memberBS.asObservable().pipe(filter(val => !!val));
}
The first line here in this service shows how I created the BehaviorSubject
. I declared it as private, so it can’t be used outside the service, and I primed the BehaviorSubject
with a null
value. Because the BehaviorSubject
is private, it is not actually accessible to any components that use this service. This is good though, because we don’t want every component to be able to update the BehaviorSubject
. We want to control how new values are emitted, so making it private is a good start. So how do we access the value from the BehaviorSubject
? That’s where the second line from above comes in to play. One of the methods of the BehaviorSubject
is asObservable
. It takes the BehaviorSubject
and creates an observable. Components can then subscribe to that observable, and it will still get any new values that are emitted from the BehaviorSubject
.
Here are a couple sample methods on the service for getting new data:
export class MyService {
// Previous stuff
getDataFromServer() {
return this._http.get('...');
}
initializeObservable() {
this.updateMemberObservableData().subscribe();
}
updateMemberObservableData() {
return this.getDataFromServer().pipe(
tap((data: Member) => {
this.memberBs.next(data);
}),
);
}
}
These are just a couple examples, but getDataFromServer
is a stubbed out function for calling some server to get the data that you will need for the Member
. updateMemberObservableData()
gets the data from the server and then emits the data through the BehaviorSubject
. initializeObservable
just makes a call to and subscribes to the updateMemberObservableData
method. This function could be called from anywhere really, but I’ve been calling it from the root AppComponent
for my application. Then, as needed, I’ll call the updateMemberObservableData()
from other components.
Once the Member
data is in the service, it’s usable all throughout the application. When a new component comes on the screen that needs the Member
data, it subscribes to the member$
observable and is on its way. It’s also made the application feel much faster as well. There’s a little delay when waiting for the member data the first time, but from then on it’s really snappy and fast.
What I really like about this way of managing state is that every component that needs that information about the member can subscribe to the observable and get the data. You don’t have to make several calls to the database to get the same information. You also don’t have to pull in a large library to manage this little piece of state for your app.
Conclusion
Storing application state in BehaviorSubjects
and exposing those as observables through a service is a great way to manage state in an Angular application. RxJS is really powerful and the more I learn about it the more I’ve found how useful it is. This same pattern can be used for other server data (like order history, for example), but it can also be used for UI state management too. An example of that would be if a navigation menu is open or closed. This pattern works for any type of state management issue. Let me know if you’ve done something similar and how it worked for you!
Top comments (0)