What is Mobx
MobX, similar to Vuex in VUE, is a cleaner state management solution that uses class decorators or functions to decorate observable, action, and computed values, and then uses the storage in the component wherever I need it. We’ll just inject the storage into the component and observe the state wherever I need it. And use it effectively.
Some of MobX’s core principles are:
- MobX can have multiple stores to store the state of an application.
- Anything that can be derived from a state without any further interaction is derived.
- Action is any code that can change state.
- All derivations are updated automatically and automatically when the state changes.
2. Mobx initialization
2.1 Mobx relies on installation
yarn add mobx mobx-react -S
yarn add react-app-rewired customize-cra @babel/plugin-proposal-decorators -D
Copy the code
2.2 Mobx Dependency Configuration
Create config-overrides. Js in the root directory
const { override, addDecoratorsLegacy } = require("customize-cra")
module.exports = override(
addDecoratorsLegacy()
)
Copy the code
Change the script of package.json to the following
"scripts": {
"start": "react-app-rewired start"."build": "react-app-rewired build"."test": "react-app-rewired test --env=jsdom" ,
"eject": "react-scripts eject"
},
Copy the code
Three. Basic use
3.1 Basic counter case
- 1. Initialize the container repository
- 2. Use mobx container state in components
- 3. Initiate action in the component to change the container state
import React,{Component} from "react";
import ReactDOM from "react-dom";
import { observable, action, makeAutoObservable } from "mobx";
import {observer} from 'mobx-react'
//1. Initialize the repository
class Store {
// Convert count into observable data
@observable count = 0;
// This line must be added in mobx6.0
constructor(){
makeAutoObservable(this)
}
@action
increment=() = > {
this.count++; }}//2. Use mobx container state in components
@observer
class App extends Component {
render(){
const {store}=this.props
return (
<>
<h1>I am ws</h1>
<h1>{store.count}</h1>{/* Call a method in the repository */}<button onClick={store.increment}>Gal.</button>
</>)}}//3. Initiate action in the component to change the state of the container
// Here we pass the Store repository to the component, connecting the Mobx repository to the component
ReactDOM.render(<App store={new Store()} / >.document.getElementById("root"));
Copy the code
3.2 Decorator syntax
Decorators are a post-ES6 class-related syntax used to annotate or modify classes and class methods. Decorators can only be used for classes and class methods, not functions, because functions have function promotions. If you must decorate a function, you can execute it directly as a higher-order function.
3.2.1 Basic use of decorators
// A decorator is a function that decorates a class
// Add a static member of the class
function fn(target){
target.foo='ws'
}
@fn
class Myclass{}
console.log(Myclass.foo);
// Decorator with parameters
@fn2(10)
class Mycfass{}
function fn2(value){
return function(target){
target.count=value
}
}
console.log(Mycfass.count); ) ;Copy the code
3.2.2 Decorator implementation inheritance
// After expanding the list object, place its attributes on the prototype of the target element
function mixin(. list){
return function(target){
Object.assign(target.prototype,... list) } }const Ws={
play(){
console.log('ws');
}
}
@mixin(Foo)
class Myclass{}// The mixin method is inherited here
new Myclass().play()
Copy the code
3.3.3 Decorators decorate class members
// Decorator decorates the inner members of the class
class Mrclass{
@readonly message='heelo'
@noenumerable bar='fff'
}
function readonly(target,name,descriptor){
console.log(target);// Prototype of the target class
console.log(name);// The name of the class member to be decorated
console.log(descriptor);// The description object of the class member to be decorated
/ / read-only
descriptor.writable=false
}
function noenumerable(target,name,descriptor){
console.log(target);// Prototype of the target class
console.log(name);// The name of the class member to be decorated
console.log(descriptor);// The description object of the class member to be decorated
// Cannot be traversed
descriptor.enumerable=false
}
const c=new Mrclass()
console.log(c.message);
for(let k in c){
console.log(c[k]);
}
Copy the code
3.3 Attribute Method
3.3.1 observables
An Observable transforms common data into Observable data. Its values can be JS primitive data types, reference types, common objects, class instances, arrays, and maps. An Observable decorates data in the hope that changes to the data trigger an update attempt
class Store {
// Convert count into observable data that can be detected externally
@observable count = 0
// Be sure to add this line
constructor(){
makeAutoObservable(this)
}
@action
increment=() = >{
this.count++
}
foo='bar'
}
Copy the code
3.3.2 rainfall distribution on 10-12 computed
Computed values are values that can be derived from existing states or other computed values. Conceptually, they are very similar to the formulas in an Excel spreadsheet. Do not underestimate the calculated values, as they help keep the actual modifiable state as small as possible. The calculated values are also highly optimized, so use them as often as possible.
class Store {
@observable price=10
@observable num=5
@computed get totalPice() {return this.price*this.num
}
}
Copy the code
<p>Total price: {store. TotalPice}</p>
<p>Total price: {store. TotalPice}</p>
<p>Total price: {store. TotalPice}</p>
Copy the code
3.3.3 action
3.3.3.1 Basic Use
To change observable data, use actions instead of stores. The way to modify directly
import React from 'react';
import ReactDOM from 'react-dom';
import {observable,action,makeAutoObservable, autorun,computed} from 'mobx'
import {observer}from 'mobx-react'
Initialize the Mobx repository
class Store {
@observable count = 0
// Be sure to add this line
constructor(){
makeAutoObservable(this)
}
@action
increment=() = >{
this.count++
}
foo='bar'
@observable price=10
@observable num=5
@computed get totalPice() {return this.price*this.num
}
@action change(){
this.count=10
this.foo='hello'
this.foo='word'}}const store=new Store()
autorun(() = >{
// This is only triggered once
console.log(store.foo,store.count,'fffrrrf');
})
store.count=20
store.foo='ttt'
store.change()
@observer
class App extends React.Component{
render(){
const {store}=this.props
return(
<div>
<h1>I'm a warehouse</h1>
<h2>{store.count}</h2>
<button onClick={store.increment}>increase</button>
<p>{store.price*store.num}</p>
</div>
)
}
}
ReactDOM.render(
<App store={new Store()} / >.document.getElementById('root'));Copy the code
3.3.4 asynchronous action
import React from "react";
import ReactDOM from "react-dom";
import {
observable,
action,
makeAutoObservable,
autorun,
configure,
runInAction
} from "mobx";
import { observer } from "mobx-react";
configure({
enforceActions: "observed"});// Initialize the Mobx repository
class Store {
@observable count = 0;
@observable num1 = 0;
@observable num2 = 0;
// Be sure to add this line
constructor() {
makeAutoObservable(this);
}
@action
increment = () = > {
this.count++;
};
//1. Define a synchronous function for modification and call it in an asynchronous function
@action.bound
changeCountAsync() {
setTimeout(() = > {
this.shngecount()
}, 1000);
}
@action.bound
shngecount(){
this.count=100
}
// Call the action function from an asynchronous function. The first argument is to change the name of the function
@action.bound
changenum1Async() {
setTimeout(() = > {
action('changnum1'.() = >{
console.log(this);
this.num1=1002
})()
}, 1000);
}
/ / 3. Use runInAction
@action.bound
changenum2Async() {
setTimeout(() = > {
runInAction(() = >{
console.log(this);
this.num2=1003})},1000); }}const store = new Store();;
autorun(() = > {
// When store.count changes. It automatically triggers that by adding an @Observable to the data member
console.log( store.count, "fffrrrf");
console.log( store.num1, "num1ffrrrf");
console.log( store.num2, "num2ffrrrf");
});
// Function call
store.changeCountAsync()
store.changenum1Async()
store.changenum2Async()
@observer
class App extends React.Component {
render() {
const { store } = this.props;
return (
<div>
<h1>I'm a warehouse</h1>
<h2>{store.count}</h2>
<button onClick={store.increment}>increase</button>
</div>
);
}
}
ReactDOM.render(<App store={new Store()} / >.document.getElementById("root"));
Copy the code
- action.bound
Bind this so that the current this points to the store. Note that action.bound is not used with arrow functions; The arrow function is already bound and cannot be rebound.
3.3.5 configure
Force action to modify data, in strict mode.
import {observable,action,makeAutoObservable, autorun,computed,configure} from 'mobx'
configure({
enforceActions:'observed'
})
Copy the code
3.3.6 runInAction
import React from 'react';
import ReactDOM from 'react-dom';
import {observable,action,makeAutoObservable, autorun,computed,configure,runInAction} from 'mobx'
import {observer}from 'mobx-react'
configure({
enforceActions:'observed'
})
Initialize the Mobx repository
class Store {
@observable count = 0
@observable foo= 'sxsddde'
// Be sure to add this line
constructor(){
makeAutoObservable(this)
}
@action
increment=() = >{
this.count++
}
}
const store=new Store()
autorun(() = >{
console.log(store.foo,store.count,'fffrrrf');
})
// Modify the data member, applicable without defining the action
runInAction(() = >{
store.count=10
store.foo='hello'
})
@observer
class App extends React.Component{
render(){
const {store}=this.props
return(
<div>
<h1>I'm a warehouse</h1>
<h2>{store.count}</h2>
<button onClick={store.increment}>increase</button>
</div>
)
}
}
ReactDOM.render(
<App store={new Store()} / >.document.getElementById('root'));Copy the code
3.4 Listening Data
3.4.1 track autorun
Auto can externally detect changes in observable data. If you want to create a responsive function that will never have an observer, use mobx.autorun. This is usually the case when you need to bridge from reactive code to imperative code, such as code that prints logs, persists, or updates the UI.
import {observable,action,makeAutoObservable, autorun} from 'mobx'
autorun(() = >{
// When store.count changes. It automatically triggers that by adding an @Observable to the data member
console.log(store.count,'gggg');
console.log(store.foo,'gggg');
}
Copy the code
3.4.2 the when
When observes and runs the given predicate until it returns true. Once true is returned, the given effect is executed and the Autorunner is cleaned up. This function returns a cleaner to cancel the autorun program ahead of time
// Execute custom logic only once if count is greater than or equal to 100
// If the condition is met, it will be executed once
when(
() = >{
return store.count>=100
},
() = >{
console.log('when=>',store.count); })3.reac
Copy the code
Rule 3.4.3 reaction
We can use this reaction function to observe changes in observable variables. It does not monitor this value when it is initially created. Reaction accepts a callback to observe the value and returns what we want. The second argument takes a value that takes the return value of the first function, which we can then perform some side effects on. The third argument takes an object with multiple options.
import { reaction, observable } from "mobx";
const todos = observable([
{
title: "eat"
},
{
title: "drink"}]); reaction(() = > todos.map(todo= > todo.title),
titles= > console.log(titles.join(","))); todos.push({title: "play" });
Copy the code
3.5 Mobx works with React Hooks
For React, we get the official binding via the Mobx-React package. But for hooks, we need to use another library, mox-React-Lite. It provides us with custom hooks that we can use directly to create observables in our component.
import React from 'react'
import ReactDOM from 'react-dom'
import { observer, useObservable } from 'mobx-react-lite'
import TodoList from './components/TodoList'
import Footer from './components/Footer'
export const App = observer(() = > {
UseObservable provides a new way to create observable, action, and computed properties in an object.
// Desired values can be accessed on this object, and components react to data changes through the Observer decorator.
const store = useObservable({
todos: [{id: 1.text: 'i am ws'.completed: true },
{ id: 2.text: 'i love qinying'.completed: false}].toggleTodo(index){ store.todos[index].completed = ! store.todos[index] .completed },get remainingTodos() {
return store.todos.filter(t= >! t.completed).length } })return (
<div className="App">
<h2>This is a todolist</h2>
<TodoList
todos={store.todos}
toggleTodo={store.toggleTodo}
/>
<Footer
remaining={store.remainingTodos}
total={store.todos.length}
/>
</div>
)
})
ReactDOM.render(<App />.document.getElementById('root'))
Copy the code
4. Record
As a rookie, I write this article with the mentality of a rookie. This document is for reference to the official website and some explorations in the learning process. The purpose of writing this document is to make a review and learning record of basic mobx knowledge during the leisure time of work projects.