Managing State in Angular: Using Services and BehaviorSubject Instead of NgRx

Managing state in Angular applications is a common challenge, and while there are numerous approaches, two stand out: using NgRx or leveraging RxJS with BehaviorSubject within Angular services. NgRx, inspired by Redux, is a robust library for managing application state in Angular. However, it comes with a learning curve and additional boilerplate, which might be overkill for smaller projects. In contrast, services combined with RxJS’s BehaviorSubject offer a more straightforward solution for applications that don’t require complex state management.

In this blog, we’ll explore why services and BehaviorSubject might be a better choice for smaller applications, when NgRx might be too much, and what happens if you opt to use NgRx for simple tasks.

What is State Management in Angular?

In any Angular application, managing and sharing data across multiple components is critical. When dealing with state, we need a way to:

  • Store and update data.
  • React to changes in data.
  • Synchronize state between different parts of the application.

In Angular, the most popular approaches for handling state are:

  • Using services with RxJS (BehaviorSubject, Observable).
  • Using NgRx, a state management library based on the Redux pattern.

Each approach has its strengths, and the choice depends on the complexity of the application.

Why Use RxJS and BehaviorSubject in Services?

RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using Observables. Angular has built-in support for RxJS and uses it extensively, especially for handling asynchronous data streams (e.g., HTTP calls). A commonly used RxJS construct for managing state in Angular applications is BehaviorSubject.

Read More: 9 Reasons to Choose Angular for Your Next Development Project

What is BehaviorSubject?

BehaviorSubject is a type of RxJS Subject that holds a current value and emits the latest value to new subscribers immediately. It’s perfect for state management because:

  • It holds a current state.
  • It allows components to subscribe and react to changes.
  • It’s simple to use and doesn’t require much setup.

Key Benefits of Using Services with BehaviorSubject:

1. Simplicity: When your application doesn’t have overly complex state requirements, services and BehaviorSubject provide a lightweight solution. Setting up NgRx with actions, reducers, and effects can be an overcomplication when you just need a few pieces of data shared across components.
With services, you just define a BehaviorSubject in your service and expose it as an

Observable for your components to subscribe to.

private alertsSubject = new BehaviorSubject<Alert[]>([]);
alerts$ = this.alertsSubject.asObservable();

2. Reduced Boilerplate: Unlike NgRx, which requires creating multiple files for state actions, reducers, and effects, services with BehaviorSubject involve much less boilerplate code. This can significantly reduce development time, especially in smaller projects.

3. Flexibility: Services can encapsulate not only the state but also the logic to interact with APIs, update the state, and handle other side effects. This makes it a more intuitive solution when you don’t need to manage complex side effects separately (like NgRx effects).

4. Out-of-the-box Compatibility: Angular developers are already familiar with services and RxJS. Since Angular itself relies heavily on RxJS, it’s natural to extend that familiarity to state management with BehaviorSubject and services.

Example: Managing Alerts with BehaviorSubject

Let’s say you want to manage a list of alerts in your application. Here’s how you could

Use a service and BehaviourSubject to handle the state:

@Injectable({
providedIn: 'root',
})
export class AlertService {
    private alertsSubject = new BehaviorSubject<Alert[]>([]); 
    alerts$ = this.alertsSubject.asObservable();

addAlert (newAlert: Alert) {
    const currentAlerts = this.alertsSubject.getValue(); 
    this.alertsSubject.next([…currentAlerts, newAlert]);
}
clearAlerts() {
     this.alertsSubject.next([]);
  }
getAlerts(){
    return this.alertsSubject.getValue();
  }
}

In this example:

  • The alerts are stored in a BehaviorSubject.
  • The alerts$ observable can be subscribed to by components to display alerts.
  • Methods like addAlert() and clearAlerts() modify the state.

This setup is easy to implement and maintain, especially for smaller applications.

Why Not Use NgRx?

NgRx is a powerful state management library that provides a structured, scalable way to handle state changes in Angular applications. It follows the Redux pattern, which enforces a unidirectional data flow and uses actions, reducers, and effects to manage the state in a predictable way. However, this level of complexity isn’t necessary for every application.

Reasons Not to Use NgRx for Simple Tasks:

1. Complexity: NgRx introduces a lot of new concepts, such as actions, reducers, effects, and selectors. These are useful for large-scale applications but can overwhelm developers working on smaller projects. If your state management needs are straightforward, using NgRx might be overkill.

2. Boilerplate Code: Every state change in NgRx requires creating an action, writing a reducer to handle that action, and possibly setting up effects for asynchronous operations. This process can lead to a lot of boilerplate code, even for simple operations like updating a list of items.

3. Steeper Learning Curve: NgRx has a steeper learning curve, especially for developers who aren’t familiar with Redux or reactive programming patterns. Implementing NgRx in a small project can be more time-consuming than using a simpler solution like services with BehaviorSubject.

4. Overhead: For small to medium-sized applications, the overhead of using NgRx can slow down development and make the codebase harder to maintain. Services with BehaviorSubject can achieve the same results with less overhead in such cases.

What If You Use NgRx for the Same Task?

Let’s consider how the same task of managing alerts would be handled using NgRx. You would need to:

1. Create Actions: First, you’d create an action for adding and clearing alerts.

export const addAlert = createAction('[Alert] Add Alert', props<{ alert: Alert }>());
export const clearAlerts = createAction('[Alert] Clear Alerts');

2. Create a Reducer: Next, you’d write a reducer that responds to these actions and update the state.

const initialState: Alert[] = [];

const alertReducer = createReducer(
initialState,
on (addAlert, (state, { alert }) => [.....state, alert]), 
on(clearAlerts, () => [])
);

3. Create Selectors: You would then need to create selectors to select the alerts from the store.

export const selectAlerts = (state: AppState) => state.alerts;

4. Dispatch Actions from Components: In your component, you would dispatch actions to add and clear alerts.

this.store.dispatch(addAlert({ alert: newAlert }));

5. Subscribe to State Changes: You would use the NgRx store to subscribe to the alerts.

this.alerts$ = this.store.select(selectAlerts);

While this setup works well for large-scale applications, it’s clearly more complex for a simple task like managing alerts. The additional setup of actions, reducers, and selectors can feel cumbersome when you don’t need such a structured system.

If you’re looking for expert Angular app development services tailored to your specific requirements, HashStudioz is here to help. Our experienced team can assist you in leveraging the right tools for effective state management and ensure your application runs smoothly. Contact us today to discuss your project and discover how we can bring your vision to life!

Conclusion: Choosing the Right Tool for the Job

NgRx is a powerful tool for managing complex state in large applications with numerous asynchronous actions and side effects. However, for smaller applications, using Angular services with RxJS’s BehaviorSubject provides a much simpler and lighter solution.

If your application has simple state management needs, such as managing a list of items or tracking a few pieces of shared data, services with BehaviorSubject offer a quick, maintainable approach. However, if your application’s state grows in complexity or requires more robust management of side effects, you may want to consider the added structure and predictability that NgRx offers.

In short, choose NgRx when your application truly requires it, and for everything else, keep it simple with RxJS and services.

Transform Your  Angular State Management Approach!

conclusion.png_1715581349988-removebg-preview (1)

Stay in the Loop with HashStudioz Blog