Easy state management in Angular
Tags: angular, observables, webdev, typescript
Published: 2022-04-21 14:10:07

Angular provides useful feature of services where we encapsulates all our Business Logic (BL) [back-end integration] inside the services. BL does includes persistence of state/data that would probably be meant for other components too. With increased components hierarchy, we tend to increase services that are associated with them which leads to application getting bloated and data communication between services and counter components gets messy.

![Spaghetti Code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/edc2hhr5lobhj8m76ndu.png)

To fix this problem, we need opinionated state management and vast majority of solutions are already available in OSS market like `NgRx, Ngxs, Akita, Elf, RxAngular` etc. But this solution comes with a cost which is their associated learning curve and boilerplate code just to have its initial setup hooked into our application.

![I'm Lost](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i93qubkkt71oqp6h9oj2.png)

To reduce this pain and get your system ready up (bare metal) and setup-ed in less time frame, I have created a dead simple state management solution in just less than 50 lines of code.

> Yes, you hear it right, its just less than 50 lines of code. 😉

![I'm Super happy now](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/annq9j5rheg9z1rseevn.png)
 

I'm not gonna say that this is a full fledged state management solution that advanced libraries does. This is a bare metal need of state management which can suffice a need of many developers in their day to day task. For obvious reason when your task and need is more complex, one should consider using an opinionated state management libraries as stated above since they are tested well within the community and are scale-able enough.

So the basic fundamental of state management is to cache recurring data which is to be passed along a lot of component hierarchy. [Input/Props drilling](https://kentcdodds.com/blog/prop-drilling) is one the issue where state management methodology like [flux](https://facebook.github.io/flux) comes to resort. A central global store that will act as hydration of data to our components and probably act as single source to truth for many entities in your application. 

So certain check list needs to be considered when implementing state management that is refer below.

✅ Central store for most of the entities (single source of truth).
✅ The store should be **reactive** (pushing instead of polling. Polling can be additional feature too).
✅ Select a certain **slice** of cached data.
✅ Update/Destroy the cached entity.
✅ No mutation for cached entity outside of [reducer](https://redux-toolkit.js.org/usage/usage-guide#writing-reducers).

The state management solution that I'm gonna present is **CRUD** based. And this is gonna suffice 70-80% of use cases.

The syntax for function wrapper is gonna remind you of slice from [Redux Toolkit](https://redux-toolkit.js.org/api/createSlice).

## Create a wrapper function
We are going to create a wrapper function that will help with the initial implementation of slice.

```typescript
export function createSlice(opts) {
}
```

### Setting up Initial Data (🇨RUD)
This is the phase where we are going to create a slice with the initial state/data.

Typings for createSlice Options would look like:

```typescript
export type CreateSliceOptions<T> = {
  initialValue: T;
};
```

Using this type inside the function wrapper.

```typescript
export function createSlice<T>(opts: CreateSliceOptions<T>) {
  let _value = opts.initalValue;
}
```


### Reading the value from inside the slice (C🇷UD)
We need to expose a function from inside the createSlice wrapper that will fetch us the current state inside the slice.

Typings for createSlice Instance would look like:

```typescript
export type CreateSliceInstance<T> = {
  ...
 /**
  * Returns the current value of slice
  */
  getValue: () => T;
  ...
}
```

Using this type inside the function wrapper.

```typescript
  return {
    ...,
    getValue: () => _value;
  }
```


### Updating the data inside of slice (CR🇺D)

In order to update the slice, we will expose a method called `update` that will update the value inside the slice.

Let's add the `update` typing to the `CreateSliceInstance`.

```typescript
export type CreateSliceInstance<T> = {
  ...
 /**
  * Callback to update the value inside the slice.
  */
  update: (state: Partial<T>) => void;
  ...
}
```

Implementing the update method in the slice wrapper.

```typescript
  return {
    ...,
    update: (state: Partial<T>) => {
      _value = state;
    }
  }
```

> How come this approach would be reactive? How would I subscribe to the changes I performed via `update` method?

In order to make our slice reactive, we need to re-adjust some implementation inside the `createSlice` wrapper, but although the typings will remain same.

> NOTE: For now, the implementation only supports objects, and not primitive like string, number, boolean etc.


```typescript
function createSlice<T>(opt: CreateSliceOptions<T>): CreateSliceType<T> {
  let _ob$ = new BehaviorSubject<T>(null);
  let _value = new Proxy(opt.initialValue ?? {}, {
    set: (target, property, value, receiver) => {
      const allow = Reflect.set(target, property, value, receiver);
      _ob$.next(target as T);
      return allow;
    },
  });
  return {
    valueChanges: _ob$.asObservable().pipe(debounceTime(100)),
    getValue: () => _ob$.getValue(),
    update: (state: Partial<T>) => {
      Object.keys(_value).forEach(key => {
        if (state.hasOwnProperty(key)) {
          _value[key] = state[key];
        }
      });
    },
  }
}
```

WOW, there are a lot of changes. Let's discuss them step-by-step:

- We have created a BehaviorSubject that will emit the value inside it whenever we trigger `next` on it.
- Instead of assigning **initalValue** directly to `_value`, we will create a new [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) object, where we will override various `handler` methods on the `target` object.
To read more about Proxy Pattern, refer [this](https://refactoring.guru/design-patterns/proxy).
- We will override the `set` method of the target object i.e. `initialValue` and will **emit** an new value, whenever a target is mutated.
- For the `update` method, we will iterate over to the properties of the supplied state as param to update method and check if the property key in the state belongs to **initialValue** object and updating the `_value[key]`. The usage of [hasOwnProperty](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) will help us eradicate any miscellaneous (unknown) property from the slice's state.
- We have use `debounceTime` in order to aggregate (iteration inside the `update` method) the changes in a certain time frame i.e. 100ms and will emit the target finally.

I hope this all makes sense to you all till now.

### Deleting/Destroying the value inside the slice (CRU🇩)

When the slice is no longer in need, we can simply destroy the slice by calling the `destroy` on it.

Typing and implementation for `destroy` would be like:

```typescript
   ...
   /**
   * Destroy the slice and closure data associated with it
   */
  destroy: () => void;
   ...
```

```typescript
return {
   ...,
   destroy: () => {
      _ob$.complete();
      // In case the target reference is used somewhere, we will clear it.
      _ob$.next(undefined);
      // Free up internal closure memory
      _value = undefined;
      _ob$ = undefined;
    },
   ...
}
```

### Resetting the slice's state (with intialValue)

There might be possibility where you might want to reset the state inside the slice.

Typings and implementation of `reset` would be like:

```typescript
  ...
  /**
   * Reset the data with initial value
   */
  reset: () => void;
  ...
```

```typescript
return {
  ...,
  reset: () => {
      const {initialValue} = opt;
      Object.keys(initialValue).forEach(key => {
        _value[key] = initialValue[key];
      });
   },
  ...
}
```

### Complete Implementation

{% embed https://gist.github.com/androizer/1245fd4a992b11efe9a298950a4be05a %}

### BONUS

If we see the implementation properly, the mutation can be possible via fetching target value from either `getValue` or `valueChanges` observable subscription. Although the **mutation should not be happening outside the reducer** (i.e. inside the slice context only).

We can fix this behaviour by wrapping the value inside the `Object.freeze(target)`. Here is the revised implementation for `getValue` and `valueChanges` respectively.

```typescript
return {
  ...,
  valueChanges: _ob$.asObservable().pipe(
      debounceTime(100),
      map(value => Object.freeze(value)),
  ),
  getValue: () => Object.freeze(_ob$.getValue()),
  ...
}
```

### Final thoughts

Thanks you for staying till here. You probably have learned something new today and that's a better version of you from yesterday's.
If you like this article, do give it a like or bookmark it for future reference. And if you feel there's need for some improvisation, do let me know in the comments. Would love to learn together.