阅读 457

mobx常用方法梳理

使用react刚好一年,一直是用的mobx进行状态管理。前一段时间在使用mobx过程中突然发现最近所使用的方式都是有一定问题的,故重新梳理其使用方式。

本文适用mobx 4或5

mobx主要是创建响应式数据,然后当数据变化时通过一些方式来做出相应的处理,常用的方法有:

  • observable
  • toJS
  • computed
  • action

下面我们来一一介绍

以上方法支持直接调用或装饰器方式,本文以装饰器来介绍。

如若不支持装饰器,则需安装@babel/plugin-proposal-decorators,并在.babelrc中配置

  {
      "plugins": [ 
          [ 
              "@babel/plugin-proposal-decorators",
              {"legacy": true} 
          ] 
      ] 
  }
复制代码

observable

将一个对象变为可观察对象,用法比较简单:

import { observable } from 'mobx'

class Store{

    @observable
    obj={a:1}

    @observable
    num=1

}
复制代码

经过observable装饰过的属性即可成为响应式数据,供之后使用。

mobx@4使用的是Object.defineProperty,学过vue@2的应该明白上面代码中obj.a是可以监测的。如若要监测一个未定义的属性obj.b,这么写是无法监测的。可以使用官方提供的extendObservable来追加监测属性。

toJS

将通过observable转换过的数据转回普通的js对象。以上面代码为前提,假设它导出了export default new Store(),那么在使用的地方通常可以这么写:

import { toJS } from 'mobx'
import store from './store'

const jsObj=toJS(store)

复制代码

jsObj已是普通的js对象,可在其他地方使用。

computed

根据现有可观察值生成一个新的值(类比vue的计算属性或vuex的getter)。用法:

import { observable, computed } from 'mobx'

class Store{
    @observable n=1

    @computed get nPlus1(){
        return this.n+1
    }
}
复制代码

以上代码中的nPlus1n变化时会重新计算,保持始终比n大1。那么怎么来改变n呢?那就要有请action了。

action

用来修改可观察数据,仅限同步。(是否想到了vuex的mutation)

import { observable, action } from 'mobx'

class Store{
    @observable order=1

    @action setOrder(val){
        this.order=val
    }
}
复制代码

不建议直接更改order的值,在需要的地方通过调用setOrder来修改。

可以通过开启严格模式来禁止直接修改。mobx.configure({enforceActions:true})

异步修改状态

那么异步呢?实际开发中需要通过接口获取值来修改页面状态可是家常便饭。可通过以下几种方式来进行异步状态修改:

在需要修改状态的地方调用已有action

import { observable, action } from 'mobx'

class Store{
    @observable order=1
    @observable order2=1

    @action setOrder(val){
        this.order=val
    }

    @action setOrder2(val){
        this.order2=val
    }

    async af(){
        const {no,no2}=await asyncFunc()
        this.setOrder(no)
        this.setOrder2(no2)
    }
}
复制代码

上栗使用async functionpromise在需要修改状态的地方调用已有action即可。

上面在await之前没有修改状态的话,是无需使用action包装的。之前一直有写,算是理解不到位了。

至此,已经可以很好的使用mobx了。使用react的话,配合mobx-react的observer便可解决大部分问题。如果想要了解更多的话,继续往下看吧。

runInAction

runInAction可以避免我们去定义很多的action来修改数据。数据的修改可以放心的用之包裹起来,修改上栗:

import { observable, runInAction } from 'mobx'

class Store{
    @observable order=1
    @observable order2=1

    async af(){
        const {no,no2}=await asyncFunc()
        runInAction(()=>{
            this.order=no
            this.order2=no2
        })
    }
}
复制代码

可以看到代码简洁了许多。

flow

flow接收一个generator,代码也非常简洁,同时也更好阅读,官方推荐。来看下写法,修改上面代码:

import { observable, flow } from 'mobx'

class Store{
    @observable order=1
    @observable order2=1

    doSth=flow(function * (){
        const {no,no2}=yeild asyncFunc()
        this.order=no
        this.order2=no2
    })
}
复制代码

在需要的地方调用doSth即可

响应数据变化

到此为止已经可以定义和修改数据了,但是否少了些什么。数据变化后我们总要处理点什么吧,比如更新视图(没错,watch没跑了)。那就有请下面几位小伙伴啦。

autorun

监测传入函数参数中用到的可观察数据:

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

class Store{
    @observable order=1
}

const store=new Store()

autorun(() => console.log(store.order));
复制代码

上面代码当order变化时,便会触发传入autorun的函数,打印出新的order。因为autorun的函数入参中用到了order(store.order)。

想要深入了解mobx会对哪些数据做出响应,可以参考:MobX 会对什么作出反应?

when

有条件的监测,接收两个参数均为函数,第一个函数返回true时便会执行第二个函数

when(()=>someCondition,()=>{ /**doSth*/ })

reaction

可以自定义需要监测哪些字段,同样接受两个参数,第一个参数返回需要监测的字段,当其变化时便会执行第二个函数

import { observable, reaction } from 'mobx'

class Store{
    @observable order=1
    @observable order2=2
}

const store=new Store()

reaction(()=>store.order,order => console.log(order));
复制代码

上栗中只有order变化时才会打印,而order2则不被监测。在某些地方使用也很方便,比如将分页数据的页码作为第一个参数的返回值,从而在第二个函数中做翻页请求(别瞅hook)。

历史原因导致项目无法升级支持hook的mobx-react。为了拥抱hook,前段时间写了个小扩展,就是用的reaction。 可以在mobx数据变化时更新hook组件。有兴趣的可以看下:

mobx-react-hook

另外,这几个响应函数

  • 会返回一个取消监测的函数const disposer = autorun(()=>{}),调用disposer()之后便可取消监测。可在组件销毁时使用。
  • 比本文多一个入参,可配置响应函数的行为,位置在最后。

更多内容参见 mobx文档

文章分类
前端
文章标签