你所不知道的React状态管理工具:mobx

594 阅读2分钟

MobX 4

原理

参考:zhuanlan.zhihu.com/p/25585910

Mobx 最关键的函数在于 autoRun

const obj = observable({
    a: 1,
    b: 2
})

autoRun里引用到了某个监听属性时(() => {
    console.log(obj.a)
})

obj.b = 3 // 什么都没有发生
obj.a = 2 // observe 函数的回调触发了,控制台输出:2
  1. 当autoRun里引用到了某个监听属性时,就会与这个属性挂钩,当这个属性发生改变时,就会执行autoRun里面的回调函数
  2. 通过使用 autoRun 实现 mobx-react:通过@observer的修饰器,在组件将外面包上autoRun,这样当组件中使用了哪些监听属性,就会与autoRun,当某个监听属性发生改变时,就会触发autoRun,直接重新render

Mobx一些api

  • 作出响应

    1. observable
    import { observable, action } from 'mobx';
    
    // observable可以定义一些被观察的属性
    // action可以定义一些具有副作用的函数,比如改变observable的值
    

    store.js

    import { observable, action } from 'mobx';
    
    class store {
    
      @observable count = 0;
    
      @action
      addCount = () => {
        this.count ++
      }
    }
    
    export default new store()
    

    也可以

    
    import { observable, action } from 'mobx';
    
    class store {
    
      @observable count = 0;
    
      @action
      addCount = () => {
        this.count ++
      }
    }
    
    export default new store()
    

    一般第一种用的更多

    1. computed 计算属性,当observable的属性修改后,才会重新计算

    2. autorun autorun用来处理副作用,当observable的值发生变化是,就会触发observable

    observable(() => console.log(this.count))
    
  • 改变 observables

    1. action

    2. 异步action:bound、flow

      mobx.configure({ enforceActions: true }) // 不允许在动作之外进行状态修改
      
      class Store {
          @observable count = 0;
      
          @action
          fetchProjects() {
              setTimeout(() => {
                this.count ++
              }, 0);
          }
      }
      

      上面的this.count ++会报错,因为不是在@action的函数中修改@observable的变量

      解决方法
      1. 可以把赋值的操作用bound绑定,写在外面:
      mobx.configure({ enforceActions: true }) // 不允许在动作之外进行状态修改
      
      class Store {
          @observable count = 0;
      
          @action
          fetchProjects() {
              setTimeout(() => {
                this.addCount(1)
              }, 0);
          }
      
          @action.bound
          addCount(num) {
            this.count += num
          }
      }
      

      尽管这很整洁清楚,但异步流程复杂后可能会略显啰嗦

      1. 在回调函数外面使用action包装
      mobx.configure({ enforceActions: true }) // 不允许在动作之外进行状态修改
      
      class Store {
        @observable count = 0;
      
        @action
        fetchProjects() {
          setTimeout(
            action('随便一个命名', () => {
              this.count
            })
            , 0);
        }
      
        @action
        fetchProjectsTwo() {
          setTimeout(() => {
            runInAction(() => this.count++)
          }, 0);
        }
      }
      

      runInAction是action的语法糖

      1. async / await

      词法上它们看起来是同步函数,它给人的印象是 @action 应用于整个函数。 但事实并非若此,因为 async / await 只是围绕基于 promise 过程的语法糖。 结果是 @action 仅应用于代码块,直到第一个 await 所以在async / await中,第一个await后面的,必须使用异步action

      @action
      async fetchProjects() {
        const { data } = await fetchGithubProjectsSomehow() // 一个获取接口数据的方法
        runInAction(() => this.count = data.count)
      }
      
      1. flow
      fetchProjects = flow(function * () {  // <- 注意*号,这是生成器函数!
        const { data } = yield fetchGithubProjectsSomehow() // 用yield代替await
        runInAction(() => this.count = data.count)
      })
      
  • 工具函数

    1. toJS 递归地将一个(observable)对象转换为 javascript 结构

    2. extendObservable

    extendObservable(storeName, {
        age: 353
    });