Modern State Management with Overmind.js
Tags: react, typescript, javascript
Published: 2020-01-27 14:44:38

Handling state in an application is like juggling with data. This article is about a very interesting way of handling state in your React app with [Overmind.js][Overmind]

## Why Overmind?

There are many tools for state management out there like [Context-API][How to use context], [Redux], [MobX] or [MST]. So why we want another one?

In my latest client project I used [context based state][How to use context] together with [Apollo Client]. I'm a huge fan of React Hooks and together with Context-API it's a very nice way to handle state in your application. It seemed to me that I didn't need any bigger state management solution like [Redux] anymore. In the beginning this was totally fine and I was happy with this approach but after two months the app became bigger and complexity was growing. I wasn't happy anymore and I decided it's time to switch to a different solution. 

Back in the days I used and liked [Redux] a lot and I felt very comfortable with it, but it always had a smell of "too-much-code" for small stuff. On the other hand I never really used [MobX] but heard only good things about it. After doing some research I found new library called [Overmind] which locked pretty interesting.

## Combining the best together

Overmind was created by [Christian Alfoni] with the goal to give the best developer experience as possible and also to have strong TypeScript support. The framework internals are hidden to the developer and the API is very simple and straight forward.

- strong TypeScript support 
- very simple API 
- good documentation and easy to learn
- great devtools

I think one of the best things you get with Overmind is that you have fully typed code nearly for free.

### Defining state

Define your state as a simple object. Even if it is Typescript you can define your state as simple like this:

```ts
const state = {
  // title: string
  title: "", 
  // count: number
  count: 0,  
  // foo: Foo | null
  foo: null as Foo | null,
}
```


> **HINT:** Don't use undefined or ? in your state because it is just considered an anti pattern. Any undefined value on a state will not show up in the devtools, it will not be passed to server or saved in local storage.

Everywhere you use the state you have full TypeScript support and code completion. SIMPLE, right?

### Derived state

Derived state are computed values based on other state. In Overmind you define your derived state directly next to the state.

Here we define a computed state variable, **doubleCount**. It is important to note that the function is memoized by default and runs only when **count** has changed. 

```ts
const state = {
  count: 0,
  // memoized function that only executed when count change
  doubleCount: (state) => state.count * 2
}
```

In [Redux] you would have to write selectors and use libraries like [Reselect] to memoize the calculation. But not in [Overmind], it's already included. SIMPLE, right?

### State mutation 

All state mutations are done with **actions**. Actions have access to your state and can directly change properties.


```ts
function incCount(state) {
  state.count = state.count + 1
  // hint: you get fully typed state and actions here
  state.count = "three" // TS Error: Should be number
}
function resetCount(state) {
  state.count = 0
}
function setCount(state, value) {
  state.count = value
}
```

> **HINT:** You can't mutate state outside the actions, this means you can't accidentally try to change them in your views.

There is no spread madness like in [Redux] that comes with **immutability**. Just change what you want to change. SIMPLE, right?

### Side effects

Effects allow you to decouple your app completely from 3rd party APIs. You can read more about them here: [overmind effects].

> **From the docs:** Developing applications is not only about managing state, but also managing side effects. A typical side effect would be an HTTP request or talking to localStorage. In Overmind we just call these effects. 

Effects should be "initialized" in the onInitialize function of Overmind. There you can provide them with all stuff they need like getters for getting current state or actions to execute.

```ts
export const onInitialize = ({ state, effects, actions }) => {
  effects.myCoolEffect.initialize({
    getSomeState: state.partOfState,
    onMoviesLoadSuccess: actions.setMovies
  })
}
```


### Access state and actions in components

To get state in a component you have to [connect][overmind connecting components] it to Overmind. This is done with the **useOvermind** hook that provides state and actions. All you have to do is to deconstruct the hook result and you have all you need. 

```ts
function Counter() => {
  // hint: you get fully typed state and actions here
  const {
    state: { count },
    actions: { incCount }
  } = useOvermind()
  
  return (
    <div>
      Count: {count}
      <button onClick={incCount}>INC</button>
    </div>
  )
}
```

This is all? Yes, it is nuts how easy it is to get state and actions into your components. But wait: How can we prevent the component from rerendering when other parts of the global state have changed, like the **title**? Our component is only interested in the **count** property and only wants to rerender if this value changes. 
Guess what: Overmind nows exactly which parts of the state the component is using and updates the component only when this part of the state changes. SIMPLE, right?

### Mutation tracking

Overmind is using mutation tracking instead of immutability, you can read more about this concept here: [immutability vs. mutation tracking].

> **Christian Alfoni:** How you detect the changes and update the UI should just work and be performant! With mutation tracking you put your faith in the system. It relieves you of the mental load of figuring out how components update, it just works and optimally so.


### Powerfull devtools

Overmind comes with very powerfull devtools. You can use the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=christianalfoni.overmind-devtools-vscode) or use the standalone version

```shell
npx overmind-devtools
```

You can see all your state and derived state, it's even possible to change it directly inside the tool. You can also see all executed actions with their payload and what part of the state they changed. Sending an action? Sure, you can do this as well. 

The view of your app is just an implementation detail. You can write and execute your whole application logic without any views, just with the help of the devtools. This is amazing and SIMPLE, right?



### Functional programming style 

I'm a big fan of functional programming paradigms, that were introduced with React and became the default in the React world after the release of hooks. Overmind perfectly fits into this. You write only functions, there is no need for classes.
When I had a look in MobX this was a big downside for me because all the examples use classes and I don't want to use classes anymore if possible.

What about pure functions? Mhhh yes sure, Overmind actions are not as pure as regular reducers. In practice it's not a real downside to me because you can test your actions very easy anyway.

### Documentation and learning curve

Overmind has a very good documentation. I read it, started to try it in my appliaction and after 3 hours I had refactored my whole app. 
The framework internals are hidden to the developer and the api is very simple and straight forward. There is no additional hidden stuff to learn like [reselect], [redux-sagas], [redux-thunk] etc.

Cool side note: this was super easy, I was very confident to do the changes because I used [react-testing-library]. The tests were testing the functionallity of the components without implementation details. I had to do only very little adjustments in test initialization.
 
After few hours you should already feel very comfortable. Back in the days it took me weeks to wrap my head around Redux. I tried also MobX (while learning Overmind) and it is much harder to read through the docs and to understand all this **observer** and **observable** stuff if this is completly new to you.


### Running examples

Here you can see a working example on Codesandbox:

<iframe src="https://codesandbox.io/embed/overmind-todomvc-typescript-39h7y?fontsize=14" title="overmind-todomvc-typescript" allow="geolocation; microphone; camera; midi; vr; accelerometer; gyroscope; payment; ambient-light-sensor; encrypted-media; usb" style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;" sandbox="allow-modals allow-forms allow-popups allow-scripts allow-same-origin"></iframe>

[Codesandbox Example TypeScript]

### Summary

I'm so happy I found [Overmind], it makes really a lot of fun to use and it simplyfied my app completely. I hope this article can help to convince you to give it a try.

#### Useful Links

- [Codesandbox Example TypeScript]
- [Overmind docs with great introduction][overmind introduction]
- [Videos of the creator Christian Alfoni himself ][overmind videos]
- [immutability vs. mutation tracking]
- Cover image by <a href="https://pixabay.com/de/users/moise_theodor-2099584/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1216853">Theodor Moise</a> on <a href="https://pixabay.com/de/?utm_source=link-attribution&amp;utm_medium=referral&amp;utm_campaign=image&amp;utm_content=1216853">Pixabay</a>


[Overmind]: https://overmindjs.org
[overmind effects]: https://overmindjs.org/guides/beginner/04_runningsideeffects?view=react&typescript=false
[overmind introduction]: https://www.overmindjs.org/introduction?view=react&typescript=false
[overmind videos]: https://www.overmindjs.org/videos?view=react&typescript=true
[overmind connecting components]: https://overmindjs.org/guides/beginner/07_connectingcomponents?view=react&typescript=false
[Christian Alfoni]: https://christianalfoni.herokuapp.com/
[How to use context]: https://kentcdodds.com/blog/how-to-use-react-context-effectively
[Redux]: https://redux.js.org/
[MobX]: https://mobx.js.org
[MST]: https://github.com/mobxjs/mobx-state-tree
[Apollo Client]: https://www.apollographql.com/docs/react/
[Reselect]: https://github.com/reduxjs/reselect
[redux-sagas]: https://redux-saga.js.org/
[redux-thunk]: https://github.com/reduxjs/redux-thunk
[Immer]: https://immerjs.github.io/immer/
[immutability]: https://redux.js.org/faq/immutable-data
[immutability vs. mutation tracking]: https://itnext.io/updating-uis-value-comparison-vs-mutation-tracking-9f6fe912dd9a
[react-testing-library]: https://testing-library.com/react
[Codesandbox Example]: https://codesandbox.io/s/overmind-todomvc-split-xdh41
[Codesandbox Example TypeScript]: https://codesandbox.io/s/overmind-todomvc-typescript-39h7y?fontsize=14