Hey guys, this post is dedicated to our recent conference presentation about using MobX in angular. We will cover following points in the article:
- What is state management and how to make it painless
- Classic observer-observable patterns and how MobX uses it
- How to apply MobX in angular application
- Couple of MobX tricks to know
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.
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.
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:
- install needed dependencies:
- Import MobX module to your Angular application module
- Set ChangeDetectionStrategy.OnPush for your components. MobX library makes sense only with OnPush strategy.
- Set this attribute for html components in template, which should be updated by MobX-Angular: *mobxAutorun="{dontDetach: true}"
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.
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}.
- Use these attributes in your typescript classes to mark data and functions: @observable, @computed, @action
Store Architecture approach
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:
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:
And Store contains @observable declarations:
Also we can have data stores, which could be used in several other stores or components as sources of data.
Store classes can have:
- @observable decorators for variables. When you put this decorator, MobX will wrap this variable into get/set code, with additional logic. When data is changed, MobX knows this and can call all ‘observers’ of this data to refresh. When you assign value to some UI component (which is under *mobxAutorun attribute of course), MobX puts this component into list of ‘observers’.
- @computed decorator is used for get functions only. This concept is similar to excel formula. For example, when you want to use some filtered data (and possibly map as well) in UI, you can incapsulate all filtering and mapping logic into @computed get function. You can also use several @observables in @computed function. When any of dependent observables have been changed, computed function will be recalculated. And the good thing is, that it will be recalculated only once per any of observables change because of caching.
- @action decorator is used for any function, where you will change value of observable.
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:
Or
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:
- you can use debug console to track all MobX actions and reactions by entering this command in browser console: mobxAngularDebug(true)
- Use extendObservable function to add new property of observable object, when it’s already in use of MobX
- Use toJS function to get clear java script object (for console log for example)
- Try to use minimum amount of .filter and .map in @computed get functions to keep code readable and debuggable.
- Just don’t forget that @observable !== Observable<any> from RxJS</any>
Helpful links
In English
- MobX documentation
- Redux in Angular
- MobX vs Redux podcast
- MobX in Angular article
- MobX-Angular Github readme
- Sample Project in GitHub You can clone it and play with Angular + MobX application
In Russian
Thank you and see you on our meet-ups!
Get 17 the Most Essential Interview Questions & Answers
Submit your e-mail to get access.