If you've ever written anything more than a very simple app with jQuery, you've probably run into the problem of keeping different parts of the UI synchronized. Often, changes to the data need to be reflected in multiple locations, and as the app grows you can find yourself tied in knots. To tame the madness, it's common to use events to let different parts of the app know when something has changed.
So how do you manage the state of your application today? I'm going to go out on a limb and say that you're over subscribing to changes. That's right. I don't even know you and I'm going to call you out. If you're not over subscribing, then I'm SURE you're working too hard.
Unless you're using MobX of course...
What is "State" Anyway?
Here's a person. Hey, that's me! I have a firstName
, lastName
and age
.
In addition, the fullName()
function might come out if I'm in trouble.
var person = {
firstName: 'Matt',
lastName: 'Ruby',
age: 37,
fullName: function () {
this.firstName + ' ' + this.lastName;
}
};
How would you notify your various outputs (view, server, debug log) of modifications to that person? When would you trigger those notifications? Before MobX, I would use setters that would trigger custom jQuery events or js-signals. These options served me well, however, my usage of them was far from granular. I would fire one "changed" event if any part of the person
object changed.
Let's say I have a piece of view code that shows my first name. If I changed my age, that view would update as it was tied to that person
's changed
event.
person.events = {};
person.setData = function (data) {
$.extend(person, data);
$(person.events).trigger('changed');
};
$(person.events).on('changed', function () {
console.log('first name: ' + person.firstName);
});
person.setData({age: 38});
How could we tighten that over-fire up? Easy. Just have a setter for each field and separate events for each change. Wait--with that you may start over-firing if you wanted to change both age
and firstName
at once. You'd have to create a way to delay your events from firing until both changes completed. That sounds like work and I'm lazy...
MobX to the rescue
MobX is a simple, focused, performant and unobtrusive state management library developed by Michel Weststrate.
From the MobX docs:
Just do something to the state and MobX will make sure your app respects the changes.
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 37,
fullName: function () {
this.firstName + ' ' + this.lastName;
}
});
Notice the difference? mobx.observable
is the only change I've made.
Let's look at that console.log
example again:
mobx.autorun(function () {
console.log('first name: ' + person.firstName);
});
person.age = 38; // prints nothing
person.lastName = 'RUBY!'; // still nothing
person.firstName = 'Matthew!'; // that one fired
Using autorun
, MobX will only observe what has been accessed.
If you think that was neat, check this out:
mobx.autorun(function () {
console.log('Full name: ' + person.fullName);
});
person.age = 38; // print's nothing
person.lastName = 'RUBY!'; // Fires
person.firstName = 'Matthew!'; // Also fires
Intrigued? I know you are.
Core MobX concepts
observable
var log = function(data) {
$('#output').append('<pre>' +data+ '</pre>');
}
var person = mobx.observable({
firstName: 'Matt',
lastName: 'Ruby',
age: 34
});
log(person.firstName);
person.firstName = 'Mike';
log(person.firstName);
person.firstName = 'Lissy';
log(person.firstName);
Run on CodePen
MobX observable objects are just objects. I'm not observing anything in this example. This example shows how you could start working MobX into your existing codebase. Just use mobx.observable()
or mobx.extendObservable()
to get started.
Continue reading %How to Manage Your JavaScript Application State with MobX%
by Matt Ruby via SitePoint