Note source: Hook Education – big front end employment Training camp

Content: Notes, thoughts and experiences in the process of learning

Tip: project actual combat article resources can not be uploaded, only for reference

MobX – Simple extensible state management

Introduction to the

A simple, extensible state management library that does the same thing as Redux, but is simpler

  • MobX is funded by Mendix, Coinbase, Facebook Open Source, and numerous individual sponsors
  • React and MobX are a powerful combination, with React rendering application state and MobX managing application state for Use by React

Browser support:

  • MobX5 runs on any browser that supports ES6-Proxy and does not support IE11, Node.js6
  • MobX4 runs on any browser that supports ES5
  • MobX4 and 5 have the same API

Version 5 is used in the course

Pre-development preparation

Enable decorator syntax support

Method 1:

  1. Eject the project’s underlying configuration: NPM Run eject

  2. NPM install @babel/plugin-proposal-decorators

  3. Add configuration to package.json file

    • "babel": {
          "plugins": [["@babel/plugin-proposal-decorators",
                  {
                      "legacy": true}}]]Copy the code

Method 2 (for course use, no error is reported) :

  1. Override the underlying configuration: NPM install react-app-rewired @babel/plugin-proposal-decorators customize-cra

  2. Create config-overrides. Js in the project root directory and add the configuration

    • const { override, addDecoratorsLegacy } = require("customize-cra");
      module.exports = override(addDecoratorsLegacy());
      Copy the code
  3. Modify configuration file package.json

    • "scripts": {
          "start": "react-app-rewired start"."build": "react-app-rewired build"."test": "react-app-rewired test",}Copy the code

Resolve vscode editor warnings about decorator syntax

  • Vscode set of javascript. ImplicitProjectConfig. ExperimentalDecorators set to true
  • Look for JS/TS obituaries with Implicit Project Config: Experimental Decorators

MobX use

  • Download MobX:npm i mobx mobx-reactMobx – ^5.15.4, mobx-react – ^6.2.2
  • Workflow: Action (command) => state(state) => views(view). If the view wants to modify the state, it must trigger action first, and state will automatically modify the view

Create and use store state

  1. The store object needs to be an instance object of a class, set its initial state inside the class, and then export the instance object
  2. Use the Modx-React Provider to pass the store object along in the same way as redux
  3. Inject the Mobx-React decorator into the component. Use that decorator to get the Store, and then get the store state in the component’s props

Use action to change the store state

  1. Write the state modification method directly in the original Store class
  2. All you need to do is fire the method in the class to change the state, and you will find that the changed state is not synchronized to the view
  3. Use the Mobx decorator Observable to turn state data into observable data
  4. The Mox-React decorator Observer is used in the component to set the component as an observer
// SRC /stores/ countstore.js create a state store

import { observable } from 'mobx'

/ / create a store
class CountStore {
  // Observable implements the observable function to create status data
  @observable count = 1
  // action
  add = () = > {
    // Change the state directly
    this.count = this.count + 1
  }
  cut = () = > {
    this.count = this.count - 1}}// Create an instance
const counter = new CountStore()
// Export the instance directly
export default counter
Copy the code
// SRC /index.js brings in and passes the state repository

import { Provider } from 'mobx-react'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import counter from './stores/countStore'

ReactDOM.render(
  // Use the Provider to pass the data along
  <Provider counter={counter}>
    <App />
  </Provider>.document.getElementById('root'))Copy the code
// the SRC/app.js component gets and uses the state and uses the action

import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'

// Receive status
@inject('counter')
// Set the observation mode to automatically update when the status changes
@observer
/ / component
class App extends Component {
  render() {
    // Destruct the state
    const { counter } = this.props
    return (
      <div>{/* Click to trigger action */}<button onClick={counter.cut}>-</button>{/* Display status */} {counter.count}<button onClick={counter.add}>+</button>
      </div>)}}export default App
Copy the code

Disallow normal functions from changing program state and introduce action decorators

To prevent any function from changing state, you need to disable these default behaviors to avoid unnecessary errors

With strict mode enabled, only action functions can change their state. After setting the Action decorator, non-action functions that want to change their state will immediately report an error

import { action, configure, observable } from 'mobx'

// Configure mobx and enable strict mode
configure({ enforceActions: 'observed' })

/ / create a store
class CountStore {
  // Observable implements the observable function to create status data
  @observable count = 1
  // add an action decorator. Functions without decorators will report an error if they try to modify the state
  @action add = () = > {
    // Change the state directly
    this.count = this.count + 1
  }
  @action cut = () = > {
    this.count = this.count - 1}}// Create an instance
const counter = new CountStore()
// Export the instance directly
export default counter
Copy the code

Correct the normal function this reference in the class

In the example above, we defined action as an arrow function. This avoids the problem of this pointing incorrectly, but in most cases, we prefer to use ordinary functions as the object action function, which requires us to set the problem of this pointing in ordinary functions

// The arrow function does not need to fix the this pointer
@action add = () = > {
  // Change the state directly
  this.count = this.count + 1
}
// Modify the this pointing problem of ordinary functions
@action.bound cut() {
  this.count = this.count - 1
}
Copy the code

Asynchronously Updating status

We cannot directly modify the state data when there are asynchronous operations in the Action function

  • Modified method 1: Modified using the runInAction() method in Mobx. The runInAction function accepts a function in which the data is modified
  • Change method 2: Use mobx’s flow function, the function accepts a function as a parameter (function*), directly modify within the parameter can be
import { action, configure, flow, observable, runInAction } from 'mobx'
import axios from 'axios'

// Configure mobx and enable strict mode
configure({ enforceActions: 'observed' })

/ / create a store
class CountStore {
  // Observable implements the observable function to create status data
  @observable count = 1
  @observable users = []
  // add an action decorator. Functions without decorators will report an error if they try to modify the state
  @action add = () = > {
    // Change the state directly
    this.count = this.count + 1
  }
  // Modify the this pointing problem of ordinary functions
  @action.bound cut() {
    this.count = this.count - 1
  }

  // Asynchronous request processing scheme 1
  // @action.bound async getUsers() {
  // const { data } = await axios.get('https://api.github.com/users')
  // // Asynchronous requests modify the status directly using the runInAction
  // runInAction(() => (this.users = data))
  // }

  // Asynchronous request processing scenario 2, using flow to handle asynchronous functions, needs to write decorators
  getUsers = flow(function* () {
    const { data } = yield axios.get('https://api.github.com/users')
    this.users = data
  })
}
// Create an instance
const counter = new CountStore()
// Export the instance directly
export default counter
Copy the code

Data monitoring

The computed value

Computed values are values that can be derived from existing states or other computed values

When there is complex logic in the template, the complex logic can be removed from the template

Computed values are processed using Mobx’s computed decorator and decorated using GET, similar to Vue’s computed properties

import { computed } from 'mobx'

// Use the modifier to change the function to the calculated value and set it to get
@computed get getRes() {
  // Return the calculated value
  return this.count * 10
}

// Use the calculated value, call directly, do not need to add () execution
{counter.getRes}
Copy the code

Autorun method

When the monitored state changes, it wants to produce the corresponding effect according to the state

It is executed once at initialization and then again when the Macy state changes

For example, when a user enters a user name, check whether the user name exists in real time

// src/stores/countStore.js mobx

import { action, autorun, configure, observable } from 'mobx'

// Configure mobx and enable strict mode
configure({ enforceActions: 'observed' })

/ / create a store
class CountStore {
  constructor() {
    // Autorun should be written in constructor
    Parameter 1: execution method, parameter 2: configuration parameter
    autorun(
      () = > {
        try {
          // Introduce a method to determine whether the current user name already exists
          UserNameDistinguish(this.userName)
          // The user name is not available to display
          console.log('Username available')}catch (e) {
          // User name error
          console.log(e.message)
        }
      },
      // Set the delay detection time to 2s
      { delay: 2000 }
    )
  }
  @observable userName = ' '

  // Change the user name action
  @action.bound changeValue(value) {
    this.userName = value
  }
}
// Create an instance
const counter = new CountStore()

// A function to determine whether the user name exists
function UserNameDistinguish(value) {
  return new Promise((resolve, reject) = > {
    // Determine the user name
    if (value === 'admin') {
      reject('Username already exists')}else {
      resolve()
    }
  })
}
// Export the instance directly
export default counter
Copy the code
/ / SRC/App. Js components

import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'

// Receive status
@inject('counter')
// Set the observation mode to automatically update when the status changes
@observer
/ / component
class App extends Component {
  render() {
    // Destruct the state
    const { counter } = this.props
    return (
      <div>{/* Text box to enter the user name */}<input
          type='text'
          value={counter.userName}
          onChange={e= > counter.changeValue(e.target.value)}
        />
      </div>)}}export default App
Copy the code