Make your Angular NgRx reducers an eye candy 🍬 using this fantastic library
Tags: javascript, angular, webdev, ngrx
Published: 2023-02-09 08:57:53

NgRx is a popular state management library for Angular applications. It provides a centralized store for storing the application’s state and a set of tools for updating the state in a predictable manner. NgRx also provides a powerful set of reducers, which are functions that specify how the state should change in response to an action.

**One of the core concepts in NGRX is immutability, which means that the state should never be modified directly**. Instead, when an action is dispatched, the reducer creates a new copy of the state with the necessary changes. This helps ensure that the state remains predictable and that it can be easily tested and debugged.

**However, creating a new copy of the state can be quite a pain, especially for complex state structures.** To address this issue, **ngrx-immer** was created, which is a library that provides an easier way to handle immutability in NgRx reducers.


![Immer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/th9dtl5j5n8x4bdlylwd.jpg)

## NgRx `on` method vs `immerOn` method

Consider a state with a list of users, where each user has a name and an age. We want to add a new user to the list, update the name of an existing user, and delete a user from the list.

Using the `on` method, we would write the following NGRX reducer:

```typescript
import { createReducer, on } from '@ngrx/store';
import { addUser, updateUser, deleteUser } from './user.actions';

export interface User {
  name: string;
  age: number;
}

export const initialState: User[] = [];

export const _userReducer = createReducer(
  initialState,
  on(addUser, (state, { user }) => [...state, user]),
  on(updateUser, (state, { user, name }) =>
    state.map(u => (u.name === user.name ? { ...u, name } : u))
  ),
  on(deleteUser, (state, { user }) =>
    state.filter(u => u.name !== user.name)
  )
);
```
Using the `immerOn` method from `ngrx-immer`, we would write the following NgRx reducer:

```typescript
import { createReducer, immerOn } from 'ngrx-immer';
import { addUser, updateUser, deleteUser } from './user.actions';

export interface User {
  name: string;
  age: number;
}

export const initialState: User[] = [];

export const _userReducer = createReducer(
  initialState,
  immerOn(addUser, (state, { user }) => {
    state.push(user);
  }),
  immerOn(updateUser, (state, { user, name }) => {
    const index = state.findIndex(u => u.name === user.name);
    state[index].name = name;
  }),
  immerOn(deleteUser, (state, { user }) => {
    const index = state.findIndex(u => u.name === user.name);
    state.splice(index, 1);
  })
);
```

As you can see, the `immerOn `method provides a more intuitive and direct way of updating the state. It allows you to modify the state directly instead of creating a new object with the spread operator.

## You don’t have to refactor your app — mix and match !

No need to rewrite all of your reducers.

You can use **immerOn** in the places that are the most complicated.

You can combine the `immerOn` method from the `ngrx-immer` library with the on method from the `@ngrx/store` library.

```typescript
import { createReducer, on, immerOn } from 'ngrx-immer';
import { addUser, updateUser, deleteUser, togglePremium } from './user.actions';

export interface User {
  name: string;
  age: number;
  premium: boolean;
}

export const initialState: User[] = [];

export const _userReducer = createReducer(
  initialState,
  on(addUser, (state, { user }) => [...state, user]),
  immerOn(updateUser, (state, { user, name }) => {
    const index = state.findIndex(u => u.name === user.name);
    state[index].name = name;
  }),
  immerOn(deleteUser, (state, { user }) => {
    const index = state.findIndex(u => u.name === user.name);
    state.splice(index, 1);
  }),
  immerOn(togglePremium, (state, { user }) => {
    const index = state.findIndex(u => u.name === user.name);
    state[index].premium = !state[index].premium;
  })
);
```
Almost no effort to use but makes a giant difference to your code readability and complexity.

![rxjs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/43a0vjzrwqzdhe2drfaf.png)

## Worthy of becoming part of the NgRx core library?

Do you think this should become a part of core NgRx library ? I vote that it should !

Why?

Installing multiple dependencies maintained outside of core libraries is always a risk of something that can stop being maintained.

Smaller lib, less maintainers = bigger risk.

And something as useful as this could come in bundle with NgRx.

Don’t agree ? Let’s discuss.

## Sources

All credits go to fantastic [Tim Deschryver](https://github.com/timdeschryver) — the author of this library.

[https://github.com/timdeschryver/ngrx-immer](https://github.com/timdeschryver/ngrx-immer)