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.