Once you get past very simple applications, programming on the web can become a nightmare of event handling and notifications, where the state of an application is spread out among dozens of different text boxes, select boxes and similar components. This is one reason why Model/View/Controller architectures (MVC) have become pretty much de rigour (along with multiple variations on this theme) in web programming.

However, MVC also requires that when you change the model, the view will change in response. This is pretty easy to accomplish when there is a direct one-to-one relationship between a component and its associated value. Where things begin to fall apart is when multiple components are dependent upon the same set of values, or when changes in values change other values that have component dependencies. This is a behavior you see quite often with spreadsheets (and one reason why translating spreadsheets into web applications can be quite a headache).

Mobx and MVC

The Mobx library makes use of ES2015 and (with later version) ES2017 decorators in order to identify specific variables or objects and make them “observable”. What this means in practice is that the framework “keeps track” of these variables, and any time the variables change, then the a notifier is activated for handling the impacts of these changes.

An example of this can be seen in the following CodePen.

See the Pen MobX+React test by Kurt Cagle (@kurt_cagle) on CodePen.

This example is deliberately kept simple to illustrate the process. Here, you can change a person’s first or last name, or change their age by using a numeric input control. This data is kept within a particular data structure, when either part of the name is changed, a new variable called flippedName is calculated, which holds an alternative medium state (in this case the name given as “lastName, firstName”). Finally, if the age of the person is 18 or higher, the flipped name appears green – the person involved can participate in whatever activity is being supported. If the age is under 17, however, the flipped name turns red, and a warning message is posted.

This example is especially useful because the data is entangled – the dispay of the output text is dependent upon both the names and the age. Moreover, the input fields make it possible to update the model, showing a “round trip” type activity that is common with MVC type applications.

Dipping into the code, the first few lines take advantage of the Babel transpiler to implement the ES2017 decorator capabilities. Decorators are pre-process directives that perform “meta-operations” on code, and are in effect the equivalent of “aspect” programming used in languages like Java. These can be used to automate logging, to set up services from functions and, in the case of mobx, to identify and manage variables and objects to be watched.

Explore React Courses

The @observable decorator is syntactic sugar for the older ES2015 implementation mobx.observable(...) . This is not a function in the traditional sense. Instead, observers act on functions and expressions of code prior to them actually being evaluated, deferring the actual evaluation until some context structures can be set up.

class Person { @observable firstName; @observable lastName; @observable age:number; constructor(_firstName,_lastName,_age){ this.firstName = _firstName; this.lastName = _lastName; this.age = _age; this.bind("firstName"); this.bind("lastName"); this.bind("age"); } @computed get flippedName() {return `${this.lastName}, ${this.firstName}` }

In this case, three properties of a given class are identified as being observable – firstName, lastName and age. This means that, when an instance of this is created, then the instance members will be observable.

The bind() function in this class is not directly mobx related. Instead, it establishes a simple binding between an input element with an id that has the same name as the variable in question. That way, if you change an input field, you also change the model, and by extension change any functionality that depends upon the variable being changed.

bind(prop,defaultValue){ document.querySelector(`#${prop}`).value = defaultValue!=null?defaultValue:this[prop]; document.querySelector(`#${prop}`).addEventListener("input", (evt)=>{this[prop] = evt.target.value}); }

An area where mobx really comes in handy is for those scenarios where you want to make computed properties. A computed property is one that has a dependency upon observable properties – when you change the observable, you also change the computed value. Here, the first and last names are inverted to create a “lastname, firstname” type string in this read-only getter. By using the @computed decorator, should the independent variables (firstName or lastName here) change, then the @computed getter is re-evaluated.

This is by itself actually a huge benefit that mobx provides. While for something like inverting name takes comparatively few cycles to perform, when you have complex structures, regular expressions, template strings, asynchronous calls or similar processes, not having to rerun a getter when nothing has changed can result in huge cycle and memory savings. The @computed decorator feature right here is well worth the price of admission to Mobx.

The mobx.autorun() decorator is one of the few that doesn’t have an associated @ property. It is one of the big workhorses of Mobx, however. Whenever an observable or computed property is changed mobx.autorun is called IF the enclosed expressions have dependencies upon those variables.

In the sample codepen, I define three “render” methods. The first displays the flipped label. The second changes the color of the inverted title from green to red if the age given is below 18, while the final method adds a warning beneath the input fields indicating when a person is too young to participate in the program.

There are three such mobx.autorun() statements, one that calls each of these functions respectively. When a user changes the first name using the input, the showLabel() and warn() functions are invoked. If they change the age, the redLabel() and warn() functions are invoked. Finally, if the last name is changed, then only showLabel() gets invoked. Why? The showLabel() function has a dependency on the first and last name, while warn() uses only the first name, and redLabel() has no dependency at all upon the last name.

The advantage of this should be obvious, especially with complex forms or applications. If a component is unchanged, then it should not be necessary to re-render it, especially when there are a lot of interdepencies acting upon that component.

In that respect, the relationship between MobX and React should be obvious. MobX creates a way to indicate when the broader (global) data model changes, notifying downstream functions when independent variables and automatically computing @computed values that can then reduce the overall computational cost. React maintains a data model at the component level, so that when state variables or attributes change, only those portions of the component dependent upon those changes gets updated.

This article focused on the basic functionality of mobx. A subsequent article will focus will look at how you can use mobx and react together to keep the total amount of unnecessary work for your application to a minimum.

Explore React Courses