从hr口中了解react的状态管理库(mobx, recoil), 立马过来学习之mobx

了解完Recoil后,立刻来学习了mobx,趁热打铁。

如果想要在react中使用mobx,我们需要安装mobx-react或者mobx-react-lite

  • 如果只想在函数式组件中使用mobx,那么只需要安装mobx, mobx-react-lite
  • 如果想要在类组件或者函数式组件中使用mobx,那么需要安装mobx, mobx-react

mobx

MobX 帮助你以一种简单直观的方式来完成工作,并且mobx中的每一个store都只能被初始化一次

image.png

mobx初体验

    // store.js
    import { makeObservable, action, observable, computed } from 'mobx'

    class Num {
      constructor() {
        makeObservable(this, {
          // 让其成为可响应式的属性
          num: observable,
          // action: 表示指定该方法是一个action方法,不让控制台报警告
          // bound: 表示自动绑定该方法的this
          up: action.bound,
          down: action.bound,
          // computed: 表示当前值是一个计算值。会存在缓存
          double: computed
        })
      }

      num = 0

      up() {
        this.num++
      }

      down() {
        this.num--
      }

      get double() {
        return this.num * 2
      }
    }

    export default new Num()
    // 使用store的值
    import React from 'react'
    import { observer } from 'mobx-react'

    import Num from '../store/index'
    function Test() {
      return (
        <div className="App">
          <p>{Num.num}</p>
          <p>{Num.double}</p>
          <button onClick={Num.up}>增加</button>
          <button onClick={Num.down}>减少</button>
        </div>
      )
    }

    export default observer(Test)

mobx的一些概念

State(状态)

状态 是驱动应用的数据。 就是我们通过mobx管理的数据。

Derivations(衍生)

任何 源自状态并且不会再有任何进一步的相互作用的东西就是衍生。

MobX 区分了两种类型的衍生:

  • Computed values(计算值)。它们是永远可以使用纯函数(pure function)从当前可观察状态中衍生出的值。感觉他特别像vuex中的getter。
  • Reactions(反应)。Reactions 是当状态改变时需要自动发生的副作用。需要有一个桥梁来连接命令式编程(imperative programming)和响应式编程(reactive programming)。或者说得更明确一些,它们最终都需要实现I / O 操作。

如果你想创建一个基于当前状态的值时,请使用 computed

Actions(动作)

动作 是任一一段可以改变状态的代码。用户事件、后端数据推送、预定事件、等等。 就是更新mobx管理的state。

mobx常见api讲解

observer

如果我们想在组件中使用mobx定义的state等,我们就需要使用observer将组件包裹。

注意这个高阶函数是在react-mobx库中的。

export default observer(Test)

makeObservable

定义store中属性和动作的配置。

 makeObservable(this, {
      // 让其成为可响应式的属性
      num: observable,
      // action: 表示指定该方法是一个action方法
      // bound: 表示自动绑定该方法的this
      up: action.bound,
      down: action.bound,
      // computed: 表示当前值是一个计算值。会存在缓存
      double: computed
    })
  }

observable

让store中的数据成为可响应式的属性。

action

表示指定该方法是一个action方法。

bound

表示自动绑定该方法的this。省去我们在使用时给该方法绑定this。

computed

表示当前值是一个计算值。会将值进行缓存。当依赖的state发生变化后,自动计算,并重新缓存。

makeAutoObservable

我们知道,在定义完state或者action以后,我们需要配置他们,让其成为一个可响应式的数据或者一个action,这就很麻烦了。

但是这个api可以自动推断我们的state和action,并自动进行配置。

  • 所有的属性都成为observable
  • 所有的方法都成为action
  • 所有的gey都成为computed

并且可以通过后续的参数来排除一些默认的这个配置。

// 第二个参数表示不适用他的默认推导,所以点击减号就会报错
// 第三个参数表示自动绑定this
 makeAutoObservable(this, { down: false }, { autoBind: true })

监听属性

autorun

自动收集使用的依赖,然后进行监听。

默认页面加载就会执行一次,当使用的state依赖发生变化,可以执行。类似于vue中的watchEffect。

autorun(() => {
  console.log('自动收集依赖,然后执行...', num1.num)
})

image.png

reaction

不会立刻执行,当监听的依赖变化时才会执行。类似于vue中的watch。

    reaction(
      () => num1.num,
      () => {
        console.log('指定依赖,然后监听执行...', num1.num)
      }
    )

image.png

mobx处理异步更新

异步操作在mobx中不需要任何特殊处理,因为不论是何时引发的所有reactions都将自动更新。

异步直接操作action方法不会有问题,数据一样会被响应。但是控制台会报警告。

  // 异步操作
  increment() {
    setTimeout(() => {
      this.num++
    })
  }

image.png 上面这个警告可以通过配置给其关闭。

    configure({
      enforceActions: 'never'
    })

以上这种方式不推荐。不要直接在action函数中异步修改state。

正确解决异步操作

  • 通过定义一个额外的函数来充当中介,调用action函数。
// 正确的异步:方式一
  incrementAsync() {
    setTimeout(this.up, 1000)
  }
  • 通过runInAction来异步更改state。
  // 正确的异步,方式二
  incrementAsync() {
    setTimeout(() => {
      runInAction(() => {
        this.num++
      })
    }, 1000)
  }

对于多个store,如何优雅的使用

我们在一个组件中可能会使用多个store中的state。所以,为了避免多次导入不同的文件,我们可以有以下处理方式

  • 定义一个统一的store出口文件,将store统一导出。
    // store/index.js
    import a from './aStore.js'
    import b from './bStore.js'

    export {
        a, 
        b
    }
  • 通过react提供的context
    import { createContext, useContext } from 'react'

    import a from './aStore.js'
    import b from './bStore.js'

    class RootStore {
      a = a
      b = b
    }

    const store = new RootStore()

    const context = createContext(store)

    export default function useStore() {
      return useContext(context)
    }

第一种方式是我们开发中常用的方法。

做一个异步小demo来测试

// person.js
    import axios from 'axios'
    import { makeAutoObservable, runInAction } from 'mobx'

    class Person {
      constructor() {
        makeAutoObservable(this, null, { autoBind: true })
      }

      person = {}

      getPersonInfo() {
        runInAction(async () => {
          const res = await axios('http://myjson.dit.upm.es/api/bins/irav')
          this.person = res.data
        })
      }
    }

    export default new Person()
// 组件使用
    import { observer } from 'mobx-react'
    import React from 'react'
    import person from '../store/test2'

    function Test2() {
      return (
        <div>
          <h1>展示个人信息</h1>
          <p>{person.person.name}</p>
          <p>{person.person.age}</p>
          <button onClick={person.getPersonInfo}>点击获取</button>
        </div>
      )
    }

    export default observer(Test2)

做一个异步小demo来测试.gif

猜你喜欢

转载自juejin.im/post/7113149982093344775