There are a lot of requirements needed to make a library well known and commonly used in React. Especially with the state-management libraries, the core concepts that the library is built upon can make a total difference in whether developers are going to adopt the framework or not.
One of the reasons why MobX was adopted by the community and used in a lot of different projects is because of its philosophy. A very straightforward, simple, robust, and unopinionated library, MobX is only getting better as time passes. A state-management library that is almost boilerplate free makes the life of developers easier, and that’s one of the major advantages of MobX over other conventional state-management libraries in React.
MobX is one of the most used state-management libraries in React and it now has a new version, introducing a few changes in order to make the library more simple, robust, and scalable. MobX is not a library exclusive for React apps, it can be used with other JavaScript libraries and frameworks as well.
We will explore what’s new in the MobX 6.0 version and see how we can migrate our code from older versions to the newest version.
In case you haven’t tried out MobX yet, I will show you the concepts that you should have in mind about how MobX really works. There are a lot of advantages of using MobX in React applications in order to make it more scalable, robust, and straightforward. From the docs.
Anything that can be derived from the application state, should be. Automatically.
Basically, MobX has four important concepts that you should have in mind:
Observables are responsible for keeping track of data structures (classes, objects, arrays, references). Whenever a value in our store changes, MobX will keep track of the new value for us:
import { observable } from "mobx"; class Store { @observable counter = 0; }
Actions are responsible for modifying our state. Whenever we want to update a value, we need to perform an action. Actions always happen in response to an event, a button clicked or a form submitted, for example:
import { observable } from "mobx"; class Store { @observable counter = 0; @action increment() { this.counter++; } @action decrement() { this.counter--; } }
Actions are responsible for changing and modifying our observables. In MobX, by default, there’s no way to update the state outside an action. This rule makes your codebase and state more predictable, bug-free, and robust.
Computed values are used for deriving information for observables. They are a function that keeps track of your state, whenever it changes, their returned values will change as well:
import { observable } from "mobx"; class Store { @observable counter = 0; @action increment() { this.counter++; } @action decrement() { this.counter--; } @computed counterMoreThan10() { return this.counter > 10; } }
Reactions are pretty similar to computed values but the difference is that it triggers side-effects and occurs when observables change, the goal of having reactions is to perform side-effects automatically.
Now that we know a little bit about the core concepts of MobX, let’s understand what has really changed in the 6.0 version and how its API has become easier and powerful to use.
A few months ago the proposal of MobX 6 was created by the creator of the library Michel Weststrate. As many proposals for new versions, there was a lot of discussion about what should be included and what should be dropped in the new upcoming version of MobX.
These are the changes and what’s new in the new MobX 6.0:
A lot of projects are known for making use of JavaScript decorators, such as Angular, NestJS, and of course MobX. Decorators are currently at stage 2 proposal and it doesn’t seem like it will be supported anytime soon.
One of the most debated points in the new 6.0 version was if decorators should be dropped in order to gain a few advantages like:
Decorators are not required in MobX anymore, although the library still could be used without decorators in the previous versions, a lot of developers didn’t like MobX at first just because the use of decorators.
For example, this is an example of MobX in the previous versions making use of decorators:
import { observable } from "mobx"; class Store { @observable counter = 0; @action increment() { this.counter++; } @action decrement() { this.counter--; } @computed counterMoreThan10() { return this.counter > 10; } }
This is an example of MobX in the 6.0 version:
import { observable, action, computed, makeObservable } from "mobx"; class Store { counter = 0; constructor() { makeObservable(this, { counter: observable, increment: action, decrement: action, counterMoreThan10: computed }); } increment() { this.counter++; } decrement() { this.counter--; } get counterMoreThan10() { return this.counter > 10; } }
For some developers, the syntax without decorators is cleaner and simpler, for others, the developer experience got worse.
The fact is that the makeObservable
utility function makes the integration easier, there’s no longer a need to download and use a lot of different packages just because of decorators.
The makeObservable
utility function should be wrapped inside the constructor
method and for each property of the state, in order to make it observable, should be specified using an annotation:
constructor() { makeObservable(this, { counter: observable, increment: action, decrement: action, counterMoreThan10: computed }); }
Decorators can still be used in MobX, but we will still need to add a constructor to the class, use the makeObservable
utility function and we can omit the second argument:
class Store { @observable counter = 0; constructor() { makeObservable(this); } @action increment() { this.counter++; } @action decrement() { this.counter--; } @computed get counterMoreThan10() { return this.counter > 10; } }
Introduced in the 6.0 version, the makeAutoObservable
utility function is way more powerful than the makeObservable
utility function.
It makes all properties observable by default, although you still can override with specific annotations:
class Store { counter = 0; constructor() { makeAutoObservable(this); } increment() { this.counter++; } decrement() { this.counter--; } get counterMoreThan10() { return this.counter > 10; } }
The makeAutoObservable
is really one of the most important and awesome changes in the new version of MobX. It makes things easier for developers, removing the need to mention each observable, action, and computed value that they might have inside a store.
MobX has a few really different ways for configuration and in order to use it in some JavaScript engines, there are a few things to be done.
In MobX 5.0, support for proxies was required and this requirement was causing a problem of support in some JavaScript engines, especially for Internet Explorer and React Native (depending on the engine).
Under the hood, MobX uses proxies to make arrays and plain objects observable. The problem is that it can cause some problems for some JavaScript engines that don’t support proxies. In the MobX 6.0, proxies are still supported and required but now there’s a way to disable it by using the configure
function:
import { configure } from "mobx"; configure({ useProxies: "never" })
It’s not an easy job to maintain and keep updating a library for many years, and the MobX community deserves the credit for it. The library keeps getting better and better and following its philosophy since the beginning, “Anything that can be derived from the application state, should be derived. Automatically”.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.