State Management — how we do it with MobX in Mobile App
Dear readers, this material was inspired by the recently completed React Native project. The project was designed with the offline first approach in mind and we used state management from the very beginning. I will cover state management ideas and principals in this text, as well as usage of MobX within react native mobile application.
Topics
- What is state management and how to make it painless and useful
- Overview of approaches
- MobX and how to apply it to React Native application
- Service / Store architecture approach
State Management
Let’s talk about state management. Basically, every data which affects how UI looks like is your application state. Like a collection of records from API which is used as a data source for the main grid and to form different filtering pane for example. The range of different independent UI elements can rely on the state of the app. And at the same time, the state can be changed by different events: new data asynchronously arrived, some UI elements or user input caused new data entered or existing data changed.
The most important task for app architect in this field is to make sure application state is consistent with UI elements. No matter what platform we are talking about. If new data arrived — all dependent UI components should reflect this change. Probably for the simple application you are able to trigger some change detection or component render manually by some change. But the bigger application is, the more complex UI relationships are, the more complicated this task becomes. As a result, data flow might become unclear and messy.
When State Management might be needed
- A reasonable amount of data changing over time
- UI Complexity
- State is comprehensive
- You need a single point of access or source of truth for state data
If some of these items sound familiar to your project, that could be a sign to deal with state management approaches. But it could be overwhelming for small or very straightforward projects. So please use common sense and add complexity only for cases where it’s worse it.
Overview of Approaches
This leads us to look into state management approaches. I would like to highlight the fact, that state management is exactly approach, or architecture principal, rather than some particular library. I would even call it the state of the developer’s mind. Some library or framework is an awesome way to support your desire to manage application state. But architecture itself is something that you are responsible for, as a solution architect.
State management approach makes us also think more in terms of data, rather than just code. State Management principles force us to bring more structure to the code, to put in place some standards how data is being updated for the whole application, making it easy to understand and more consistent.
Flux / Redux Principles
Redux is one of the most popular and widely used frameworks for state management. It uses so-called flux principles, described below:
- Unidirectional data flow
- The state is a single source of truth
- The State is immutable
- Operates with items like actions, stores, views, reducers, subscriptions
Basically, with Redux developer defines what is state by creating a store, writes functions to change state by type of action, and manage who is listening for state changes. The most important idea here is that the state is immutable. We cannot change the state directly, we can only create a new one using a defined function. This means that the state is always consistent. There is no chance the state will be incorrect unless reducer function contains bugs.
Reducer is a defined function which contains logic to create a new state based on action.
The store contains a state management code by Redux.
You can manually subscribe or unsubscribe for state events and add own handling logic there. But for UI updates we usually use React-Redux library or similar connector, which do the same automatically.
Observer-Observable Pattern
Moving on, there are other approaches to track and manage state. Let’s take a look at one of the classical software patterns — Observer-Observable — simple and powerful enough.
It has so-called ‘Observable’, which is essentially our state, but with some additions. Other objects can ‘subscribe’ to state changes. ‘Observable’ object stores the list of its subscribers. When ‘Observable’ value changes, ‘Observable’ iterates list of ‘observers’, calling some kind of ‘handleChanges’ method for each.
In fact it very easy to explain in excel sheet terms. There is a data cell and formula cell. The data cell is ‘Observable’, formula — ‘observer’. Data cell knows which formula cells use it. When value changes, this cell notifies every dependent formula and asks to re-calculate.
MobX
Let’s talk about MobX. This library uses ‘Observer-Observable’ pattern under the hood. This basic and pretty simple idea forms the core of MobX. From my personal perspective, this is a versatile and useful state management library. It’s being updated constantly, documentation is in very good shape, and the entry level is absolutely minimal.
MobX operates the following ideas:
- A state is mutable. When you need to change it — just change it
- A state is anything you want. Full support of Object Oriented Approach
- Can be integrated into any architecture approach, because implements one particular independent function. You can decide how to use it.
- It’s JS library. Supports Angular 2+, React, React Native
MobX in React Native
We are getting closer to real life sample, how to use this inside React Native application. Perform these actions to integrate MobX in React Native application:
yarn install mobx
yarn install mobx-react
- Use MobX decorators in Store Class for state
Use MobX decorator in Component / Container Class for State Observer
MobX Understanding
- @Observable — state object.
@observable
decorator is used for variables. When you put this decorator, MobX creates get/set property with additional logic instead of this variable. When data is changed, MobX notices this and calls all ‘observers’ of this data to refresh (to render). When you put an @observable
value into some UI component (which is under @observer
decorator), MobX puts this component into the list of ‘observers’.
- @Computed — get function for state representation or the aggregation
@computed
decorator is used only with get functions. 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 encapsulate all filtering and mapping logic into @computed
get function. You can also use several @observable
values in the@computed
get function. When any of dependent observables has been changed, the computed function will be re-calculated. And the good thing is, that it’s cached. What means, that it will be re-calculated only once per any change.
- @Action — function to change state
@action
decorator is used for any function, which changes state, or value of the@observable
variable. @action
behaves as the transaction for state change. If you change several fields in the state object inside @action
function, MobX will track everything as the only one single change, leading to only one refresh of UI.
- @Observer — decorator for usage in UI components or other subscribers of the state changes.
UI Component gets notified when the state is changed and asked for refresh its view. In case of React — MobX will call the render function, for Angular — trigger change detection.
MobX supports mutable state
One of the main MobX concepts is that state is mutable. MobX expects you to change the state variables. You can use classes and properties for observable variables with any depth or use arrays. Change of data can look simply like this:
data.array[i].prop1 = 'new value';
Ordata.array.push(newValue);
MobX is waiting that you will actually change values of existing @observable
variables and then perform own magic with calling every observer to be updated (to render).
MobX Tricks to Know
Observable behavior:
- Till version 4
@observable
arrays behaved differently from regular JS objects - MobX 5 introduced the usage of JS Proxy for objects.
- Starting from MobX 5 all arrays wrapped with
@observable
are recognized by code and other libraries as regular arrays. This will help to make code easier for the number of cases - MobX 5 now detects adding new properties to the object without using of the additional functions like
extendObservable
Limitations of MobX 5:
- Now only ES5+ is supported, some old browsers will not support MobX 5
- React Native for Android — need to use the latest JS Core version
- Node.JS 6+
React Higher-Order Components issue:
- Use Higher-Order Components as Components, NOT as functions, in order to make MobX working as expected (otherwise it will be too much change events, keyboard blinks and so on)
Store Architecture approach
MobX can be used with any architecture, it makes sense to bring some structure into how developers organize code and data flow inside the application. We suggest using the service/store approach as in the picture below. Let’s take a look.
Principal architecture parts and their ideas:
- Service is used to be called from the container. Service can get data from API and put it into the store
- Store (backend store) is used to keep data from API. MobX works here, the store contains
@observable
variables,@action
functions and@computed
properties. Can be used across several containers or view models - ViewModel brings all data from stores to the container, with appropriate aggregations and/or filtrations. ViewModel is usually one per container. But it depends on the complexity and other factors. MobX works here as well
- Container calls service, put data from view model to View Component as react props. Should be marked with
@observer
decorator - View Component has template, styling and all about visualization. No state here, it’s dependent only on input props
How can I apply all this easily?
With special care, we have prepared sample React Native based project for you to try this architecture approach and MobX state management. All you need is to clone Sample Project in GitHub. It contains simple and easy to understand React Native starter project, which is developed with service/store architecture in mind and utilizes MobX as state management library.
Try it and probably you can make your next project faster and better in terms of data flow and state management!
Helpful links
- MobX documentation
- Redux documentation
- MobX 10 minutes Introduction
- MobX vs Redux podcast
- MobX in React
- MobX in React Native
Published initially in Akveo Engineering Medium Blog