State Management with a Single Line of Code
Tags: javascript, webdev, productivity, typescript
Published: 2020-11-02 21:27:31

If you're like me and feel that there has to be an easier way of state-management, then you'd like what [ActiveJS](https://activejs.dev/) can do for you.

I feel like I'm selling snake-oil, but I spent the last 10 months trying to make state-management as intuitive and easy as possible because I couldn't stand the state-management in the state it is right now.

For efficient state-management, we need a few things
- data structures that are **type-safe**
- data structures that can **emit events on mutation**
- data structures that can **guarantee immutability**
- data structures that can be **persisted through sessions**

The title promised all this in one line of code, so here it is.
```ts
const dataUnit = new DictUnit({
 id: 'data', immutable: true, persistent: true, cacheSize: Infinity,
 initialValue: {a: 1}
})
// every option is optional, including the initialValue
// DictUnit has empty object {} as it's default value
```
(Okay 4 lines, but I formatted it so you don't have to scroll :)

JavaScript doesn't have anything like that, that's why ActiveJS came into existence, and with it came reactive data structures called [Units](https://docs.activejs.dev/fundamentals/units), one of them is [DictUnit](https://docs.activejs.dev/fundamentals/units/dictunit), that stores and ensures a dictionary object value at all times.

You might have already got a feeling from the configuration options we passed to the DictUnit and guessed what it's all about, but to elaborate DictUnit is:

- Observable
- Reactive
- Type-Safe
- Immutable
- Persistent, and
- Cache-Enabled

Let's see what that means in the language we all understand, the code:

### Observable
[DictUnit](https://docs.activejs.dev/fundamentals/units/dictunit) extends RxJS Observable class, so you can subscribe to it and apply all the RxJS operators on it just as you would on an Observable.
```ts
// subscribe for the value
dataUnit.subscribe(value => console.log(value))
// logs {a: 1} immediately and will log future values

dataUnit instanceof Observable; // true
```

### Reactive
When you update the value of a DictUnit it emits it to all the observers so that they get access to the latest value.
```ts
// non-functional dispatch
dataUnit.dispatch({b: 2, c: 3})
// observers get {b: 2, c: 3}

// now dataUnit's value is {b: 2, c: 3}

// functional-dispatch
dataUnit.dispatch(value => {return {...value, d: 4}})
// observers get {b: 2, c: 3, d: 4}

// we don't have to dispatch new values manually,
// DictUnit provides a better way to update properties

// update a single property
dataUnit.set('d', 5)
// observers get {b: 2, c: 3, d: 5}

// delete properties
dataUnit.delete('b', 'd') // 'b' and 'd' got yeeted
// observers get {c: 3}

// update multiple properties
dataUnit.assign({a: 1, b: 2})
// observers get {a: 1, b: 2, c: 3}
```

### Type-Safe
A DictUnit ensures that at all times the value is always a dictionary object, it'll ignore any invalid value dispatch.
```ts
dataUnit.dispatch(['let', 'me', 'in']); // won't work
dataUnit.dispatch('let me in'); // won't work
dataUnit.dispatch(420); // won't work
dataUnit.dispatch(null); // won't work
dataUnit.dispatch(new Date()); // won't work
dataUnit.dispatch(() => new Date()); // won't work
```
There are 5 other Units just like DictUnit in ActiveJS, [ListUnit](https://docs.activejs.dev/fundamentals/units/listunit) to store `array`, [NumUnit](https://docs.activejs.dev/fundamentals/units/numunit) to store `number`, [StringUnit](https://docs.activejs.dev/fundamentals/units/stringunit) to store `string`, [BoolUnit](https://docs.activejs.dev/fundamentals/units/boolunit) to store `boolean`, and [GenericUnit](https://docs.activejs.dev/fundamentals/units/genericunit) to store anything.

### Immutable
The immutable flag makes sure that the DictUnit doesn't let the value get mutated in any way. Let's try to mutate it anyway.
```ts
const newValue = {c: 3};
dataUnit.dispatch(newValue) // works, value is {c: 3} now

// try mutating the newValue
newValue.c = 'hehe' // works, but
dataUnit.value() // still {c: 3}

// let's try a different approach
const currentValue = dataUnit.value() // {c: 3}
currentValue.c = 'gotcha' // works, but
dataUnit.value() // still {c: 3}
```

### Persistent
The persistent flag makes the DictUnit persistent, such that whenever its value is updated, it saves that value to LocalStorage, so if we reinitialize a DictUnit with the same `id` and `persistent: true` flag, the DictUnit will restore its value from LocalStorage.
```ts
dataUnit.dispatch({c: 4}) // saved in LocalStorage

// after refreshing the browser-tab or reinitializing the DictUnit
dataUnit.value() // {c: 4}
// it restored the value from LocalStorage
```

### Cache-Enabled
What if I told you that we can go back to all the previous values we just updated in previous examples, and then come back to the current value, yup Time-Travel is possible. All you need to provide is how many steps you want to be able to go back using the `cacheSize` option, by default it keeps 2 values and supports up to Infinity.
```ts
// let's reinitialize the Unit to demonstrate cache-navigation
const dataUnit = new DictUnit({
 cacheSize: Infinity, initialValue: {a: 1}
})
// now let's dispatch a bunch of values to fill the cache
dataUnit.dispatch({b: 2})
dataUnit.dispatch({c: 3})
dataUnit.dispatch({d: 4})
dataUnit.dispatch({e: 5})

// now the value is {e: 5}, and
// the cache looks like this [{a: 1}, {b: 2}, {c: 3}, {d: 4}, {e: 5}]

// go back 1 step
dataUnit.goBack()
// now value is {d: 4}

// go back 2 steps
dataUnit.jump(-2) // negative means back, positive means forward
// now value is {b: 2}

// jump to the last value in cache
dataUnit.jumpToEnd()
// now value is {e: 5}

// jump to the first value in cache
dataUnit.jumpToStart()
// now value is {a: 1}

// go forward 1 step
dataUnit.goForward()
// now value is {b: 2}
```

That's it, folks, all done.

There are still a few things that we haven't covered that DictUnit can do, and we also haven't covered things like managing asynchronous API calls. But maybe that's a topic for the next article.

In the meantime, stay safe, try to have fun, and head over to [ActiveJS website](https://activejs.dev) or [documentation](https://docs.activejs.dev) to learn more about how it can help you manage state with minimum effort.

Here's the [StackBlitz playground link](https://stackblitz.com/edit/typescript-gr9zqv?file=index.ts) if you want to try it out yourself.

Here's a [link to the visual playground](https://activejs.dev/#/playground/dictunit), which you can try out without writing any code.

Also, I forgot to tell you that this is my first ever article on any platform, please let me know if I did an okay job, or if there's something that I can improve.

Cheers

[🌏 ActiveJS Website](https://activejs.dev)
[📖 ActiveJS Documentation](https://docs.activejs.dev)
[🤾‍♂️ ActiveJS Playground](https://activejs.dev/#/playground/dictunit)
[💻 ActiveJS GitHub Repo](https://github.com/activejs/activejs) (drop a ⭐ maybe :)

Next Read: [Asynchronous State Management with ActiveJS](https://dev.to/dabalyan/asynchronous-state-management-with-a-single-line-of-code-2999)