React’s state management tools include Redux and Mobx. As a newbie who has never used either of them before, after a series of not very deep understanding, I found that Redux was a bit expensive to learn and had a bit more boilerplate code. After a short thought, I decided to use Mobx as the state management tool for my next project.

But… Due to the lack of understanding, it was no surprise that I stepped on a lot of pits when building this project. Below, I will record my learning process (stepping on pits) for the convenience of newcomers to avoid timely and carefully knock code.

Step 1: Mobx basic knowledge learning

@ observables decorator

Mobx uses a class-definition syntax to put the state that needs to be managed into a class, and uses the @Observable decorator to transform objects into observable objects. Such as:

class store {
    @observable title = ' '
}
Copy the code

Observable modifies JS basic data types, reference types, common objects, class instances, arrays, and maps.

It should be noted that complex data structures such as arrays, objects, and maps modified by @Observable are called (Observable) objects. For example:

Class store {@observable todos = [1,2,3] console.log(array.isarray (this.todos))}Copy the code

The result will be false, and although it is no longer of type array, it can use all array methods and access all array elements normally. If you want to convert an Observable to a primitive type, you can use the toJS function.

ToJS is a utility function provided by Mobx. It can recursively convert an observable object to a javascript structure using the following method:

import { toJS } from  'mobx'Class store {@observable todos = [1,2,3] console.log(array.isarray (toJS(this.todos)))}Copy the code

The result will then be true

@computed

@ Computed attributes that derive other values based on the current value or state, for example:

class store {
    @observable firstName = ' '
    @observable lastName = ' '
    @computed get fullName () {
        return this.firstName + ', ' + this.lastName
    }
}
Copy the code

autorun

Use mobx.autorun when you want to create a responsive function that will never have an observer. When observable data changes, Autorun automatically performs behavior dependent on observable data. Such as:

class Store {
    @observable firstName = ' '
    @observable lastName = ' '
}
const store = new Store()
autorun(()=>{
    console.log(store.firstName + ', ' + store.lastName)
})
store.firstName = 'lily'
store.lastName = 'martin'
Copy the code

When executed, autorun executes three times, each time you change firstName or lastName

Autorun and computed: If you want to generate a value in a responsive manner that can be used by other observers, use @computed. If you want to achieve an effect without generating a new value, use Autorun

@action

@Action is anything you can use to change state, and @action.bound can also be used to automatically bind actions to target objects. Such as:

class Ticker {
    @observable tick = 0

    @action.bound
    increment() {this.tick++ //this is always correct}} const ticker = new ticker ()setInterval(ticker.increment, 1000)
Copy the code

Mobx injection

After learning basic Mobx knowledge, I created two store files to store user-related data and order-related data respectively. The following is an example:

// store/user.js
class userStoreClass {
    @observable userId = ' '
    @observable phone = ' '. } const userStore = new userStoreClass()export default userStore
Copy the code
// store/order.js
class orderStoreClass {
    @observable orderId = ' '
    @obervable orderTime = ' '. } const orderStore = new orderStoreClass()export defalut orderStore
Copy the code

Two stores are then injected into the index

// index.js
import { Provider } from 'mobx-react';
import orderStore from './stores/order'
import userStore from './stores/user'const stores = { orderStore, userStore }; ReactDOM.render( <Provider {... stores}> <App /> </Provider>, document.getElementById('root'))
Copy the code

Mobx use

First introduced

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

@inject('userStore')
@observer
class ViewBox extends React.Component {
}
Copy the code

Observer: The Observer function/decorator can be used to turn the React component into a responsive component. It wraps the component’s render function in mobx.autorun to ensure that the component can be forced to refresh whenever the data used in any component rendering changes. The Observer is provided by a separate Mox-React package.

Inject: Starting with Mobx-react4, to connect to these stores, you must pass a list of store names to inject, which allows stores to be used as props for components.

Once the reference is complete, we can use the data and methods in the Store directly in prop. Such as:

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

@inject('userStore')
@observer
class ViewBox extends React.Component {
    render() {
        return<div>{this.props.userStore.phone}</div>; }}Copy the code

Phase summary:

After all this, MOBx was ready and happy to write code on it, but while I was writing it, I remembered to try React hooks.

It is well known that hooks are a major update to React in recent years. Hooks must be used in function components, but how do function components inject Mobx? We tried referencing the class component directly and found that it worked. What to do? Looking through Mobx’s Chinese documentation, not a single trace of hooks is found. Having to endure a skull ache while scrolling through official documents…

Mobx + hooks exploration

A fierce search such as tiger, finally I found ~ ~

First link: mobx-react.js.org/libraries#c…

First, there are several official recommendations for hooks:

  • If you haven’t used hooks yet, feel free to use mox-reac@5
  • If you want to migrate your project to hooks that have both functional and class components in your project, you can use includemobx-react-litethemobx-react @ 6And use it automatically for functional components.
  • If your project uses functional components entirely, use them directlymobx-react-liteIt supports hooks and is faster and smaller!

So how do we use mobx with hooks? Link: mobx-react.js.org/recipes-con…

Since our project basically involves Multiple stores, I will directly look at the section of Multiple Global Stores. Combined with our above example, the process is consistent with the official example.

Start by creating two repositories

// store/user.js
export default class userStoreClass {
    @observable userId = ' '
    @observable phone = ' '. }Copy the code
// store/order.js
export default class orderStoreClass {
    @observable orderId = ' '
    @obervable orderTime = ' '. }Copy the code

Note: Unlike the above example, I’m exporting class instead of an instance of class, because the official documentation states:

Saving class instances globally is considered a bad practice because it can cause problems during snap-ins and integration tests for some applications.

The class cannot be directly injected into the project. It must be instantiated first

// index.js
import { Provider } from 'mobx-react';
import orderStoreClass from './stores/order'
import userStoreClass from './stores/user'Const stores = {orderStore: new orderStoreClass() userStore: new userStoreClass()}; ReactDOM.render( <Provider {... stores}> <App /> </Provider>, document.getElementById('root'))
Copy the code

So we can continue to use Mobx in the class component

To continue compatibility with hooks, we will create a storesContext to manage our store:

// src/contexts/index.js
import React from 'react'
import orderStoreClass from './stores/order'
import userStoreClass from './stores/user'

exportConst storesContext = react.createcontext ({orderStore: new orderStoreClass() userStore: new userStoreClass()})Copy the code

Finally, let’s use the custom useStores hook to access the exported storeContext value.

// src/hooks/use-stores.js
import React from 'react'
import { storesContext } from '.. /contexts'

export const useStores = () => React.useContext(storesContext)
Copy the code

Now we can use stores in functional components

import React from 'react'
import { observer } from 'mobx-react'
import { useStores } from '.. /hooks/use-stores'

// src/components/Counter.js
export const Counter = observer(() => {
  const { userStoreClass } = useStores()

  return (
    <>
      <div>{userStoreClass.userId}</div>
      <button onClick={() => userStoreClass.add()}>++</button>
      <button onClick={() => userStoreClass.toggle()}>--</button>
    </>
  )
})

Copy the code

The above