Application State Management with MobX in Angular

angular mobx state observable computed

Hey guys, this post is dedicated to our recent conference presentation about using MobX in angular. We will cover following points in the article:

State Management

What is state management? As you might know or guess application state means all current information and data, which is essential to the application and affect visual part of application. Any UI change, user actions, buttons clicks and so on change the state of application. And this means, that application should update other UI parts and components, to keep itself consistent with the state.

The ancient approach of dealing with it - manual implementation of every actions in javascript (assuming we talking about web applications). It leads to tons of javascript code, click handlers and complexity of change and refactoring.

This is exact point where all familiar with Angular 2+ people should scream and shout that Angular is clever enough to handle all that by itself. And yes it is. But only in case of default change detection strategy, when Angular updates everything every time. If you have rather small application - go with it. But in case if application is big enough, you have bunches of UI components to show a lot of data, you might notice performance overhead and some slowness. And still data relations will be unclear from the code.

This leads us to look into state management approaches, like MobX. Such libraries (or you can think of it more like design pattern) force us to think in terms of data and it’s relations, rather than code. These kind of approaches brings more structure to UI code, makes refactoring easier and getting into the project faster. I would like to mention performance as well in some cases. When you use OnPush strategy in angular, it will not update UI components every time. Please check out article with more details by link at end of this post. And MobX will help Angular a little bit, putting needed components to list for update during next change detection.

Observer-Observable Pattern

This is classical pattern, simple and powerful enough. Let’s take a look at the picture.

Observer Observable Pattern

It has so called ‘Observable’, which is essentially our state, but with some additions. Other entities can ‘subscribe’ to it’s changes. ‘Observable' entity handles the list of it’s subscribers, and when value changes, ‘Observable’ iterates list of ‘observers’, calling method ‘handleChanges’ for each.

Excel Sample

In fact it very easy to explain in excel sheet terms. There is data cell and formula cell. Data cell is ‘Observable’, formula - ‘observer’. Data cell knows which formula cells uses it. When value changes, this cell notifies every dependent formula and asks to re-calculate.

MobX in Angular

Perform these actions to integrate MobX in Angular application:

npm install mobx  
 npm install mobx-angular
import { MobxAngularModule } from 'mobx-angular';

 @NgModule({
    declarations: [...COMPONENTS],
    imports: [MobxAngularModule ...],
    bootstrap: [AppComponent]
 })
 export class AppModule { }
<div *mobxAutorun="{ dontDetach: true }">  
 ...
 </div>

Detach / Don't Detach

Additional setting dontDetach is not documented feature of MobX, needed for Angular applications. And this is a bit confusing, because different parts of MobX-Angular documentation contradict to each other.

MobX-Angular Changelog

The idea is, that you can use just *mobxAutorun attribute. But for performance reasons you can use it with parameters {{detach = true} for example) or use OnPush strategy. This means, that component will be detached from Angular change detection and behave on it's own. But according to experience, it might cause component not to be updated in some cases, especially when component structure is complicated. That's why in prod applications we use both OnPush and {dontDetach = true}.

@observable
 state: any = {
   spendings: [],
   spendingTypes: []
 };

Store Architecture approach

Store Architecture

Suggested Store approach to use with MobX is like described in a picture. Let’s dive in a little bit. We applied such pattern in several production projects.

Each Angular component has View (html template), component class and store. This store contains observable variables and other MobX stuff, needed for particular component only. This leads to clear separation of data logic and everything else. Store contains only code and declarations, which are related to application state.

View of the component (or any of component’s parents) should contain MobX attribute:

<div *mobxAutorun="{ dontDetach: true }">  
 ...
 </div>

It’s enough to put this attribute on top of all components, but usually it makes sense to put it only in parts, which requires MobX observing.

Component class is regular Angular component:

@Component({
   selector: 'app-component',
   templateUrl: './component.html',
   changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class Component {
   constructor(public store: Store) {}
   changeFunc() {...}
 }

And Store contains @observable declarations:

@Injectable()
 export class Store {
   @observable state: any;
   @computed get aggregation() {...}
   @action changeFunc(data) {...}
 }

Also we can have data stores, which could be used in several other stores or components as sources of data.

Store classes can have:

Good thing about MobX is that it’s designed to let you use classes and properties for observable variables with any depth or arrays. Change of data can look like this:

data.array[i].prop1 = 'new value';  

Or

data.array.push(newValue);  

MobX is looking for such changes and accepts mutable state strategy. In other words, MobX expect that you will actually change values of existing @observable variables or it’s inner properties.

MobX tricks to know

You might need these small things to be equipped with when using MobX:

In English

In Russian

Thank you and see you on our meet-ups!