Angular State Management in RxJS Services

Posted on March 03, 2020

Written by Preston Lamb

angular rxjs state management

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!

Click here to subscribe to the newsletter and be the notified when a new blog post is available!