One of the key components of applications is state-management. State-management refers to managing user interface(UI) controls such as text fields and buttons – it involves retrieving, saving, and rendering data from the UI controls.
For state-management, JavaScript has a library called MobX. MobX is a straightforward, scalable library that works well alongside React. MobX provides the mechanism needed to store and update the application state that React uses to render components.
Below is the gist of how MobX operates:
An Event causes an Action and an Action is the push that is needed to change the observable State. Actions are basically functions that modify the application state. MobX supports a uni-directional flow of data and ensures that all the changes affected by the Action are automatically updated. These automatic updates are the side-effects of the Action.
Observe the code below:
@action onClick = () => {
this.props.todo.done = true
}
The onClick
function is decorated with @action
. Here, we can see how an action (onClick
) changes the State of this.props.todo.done
to true
. This alteration will have further side-effects connected to it.
Every application has data structures like arrays and objects. The State of these data structures is maintained by adding observable capabilities to them. When the objects are observable, they can broadcast new changes for the observer can to react to.
An object can be made observable by decorating the object with @observable
:
class Todo {
id = Math.random()
@observable title = ""
@observable finished = false
}
Here, we have two observable containers that hold the State of title
and finished
. Making an object observable is similar to making it a spreadsheet cell. When you change the value of a particular object/cell, the change is broadcasted to all the other objects/cells that depend on the State of that particular object/cell.
Computed Values are the values derived from the application State. These values are computed from the already defined observable. These computed values can range from simple values, like the number of unfinished todos, to complex stuff, like a visual HTML representation of your todos.
To derive values when a State is modified, we can use the @computed
decorator:
@computed get unfinishedTodoCount() {
return this.todos.filter((todo) => !todo.finished).length
}
Here you can imagine unfinishedTodoCount
as the derived spreadsheet cell that will be modified as soon as the State of the cell has finished
the changes.
Reactions are somewhat similar to Computed Values except that Reactions are a side-effect of a change in application State, and no new value is produced. A reaction produces a side effect for things like printing to the console, making network requests, incrementally updating the React component tree to patch the DOM, etc.
There are three types of Reaction functions:
The autorun function runs whenever it detects a change in state. There is no need to explicitly state the change of state, the observable object inside the autorun function does the job:
autorun(() => {
console.log('delay is', this.count);
} );
Here, as soon as the count
object’s value changes, the msg 'delay is {count}'
is logged.
The when function is used to give a Reaction when there is a specific change of State. The when function takes in two arguments; the first one gets reevaluated until it returns a true state, and the second one serves as the side-effect when the first parameter returns true:
when(
// once...
() => this.count > 60,
// ... then
() => console.log("Guest is too late, maybe he's not coming");
);
Here, the first parameter is evaluated until the count
object is greater than 60. As soon as the condition is met, the second parameter is triggered and the statement is logged.
Reaction is similar to autorun. The reaction function again two arguments. The first argument watches the changes in the objects and returns it so that it is used as the input for the second argument. The second argument auto carries the necessary side-effects whenever there is a new input value. You can also give another argument to dispose of the reaction:
reaction(
() => this.count,
(count, reaction) => {
console.log("reaction demo: invoked. delay is " + count);
reaction.dispose();
}
);
The value of the variable count
is fed to the second function as soon as it is updated. The second function logs the statement and then disposes of the reaction during execution.
Free Resources