Mobx入门指南

Mobx简介

Mobx是一种轻量级的状态管理, 可以根据应用的 UI、数据或业务逻辑来组织store

Mobx与Redux的对比

Mobx Redux
多个store store为单一数据源
state可读可写,更新简单 state只读,需要写 action,在 View 层调用 action,根据相应 action type,写对应 reducer返回新state
只有用到该数据的地方才会更新 任何action dispatch都会广播,需要自己用需要我们在组件中用 shouldComponentUpdate 控制更新粒度

为何用Mobx取代setState

  • setState是异步操作,使用不当会导致bug
  • setState会造成一些不必要的重新渲染
  • setState是不能满足所有的状态存储的,比如如果将一些权限管理状态放在state里面就会造成不必要的渲染

具体可以参考3 Reasons why I stopped using React.setState

observable

把基本数据类型,引用类型,普通对象,类实例,数组和映射变成可观察属性。

observable有两种用法:

  • observable(value)
  • @observable classProperty = value

实例:

//将数组传给observable,对list做的操作都是视为可观察的,如push,shift等
const list = observable([1,2,4]);
//将object设为可观察的,只有初始化时存在的属性才会转化成可观察属性
const obj = observable({ 'price': 5 });
//创建key是动态的可观察对象
const obj = observable.map({ 'price': 5 });
obj.set('name': 'apple');

//类属性中声明可观察属性
import { observable, computed } from "mobx";
class OrderLine {
    @observable price = 0;
    @observable amount = 1;
    
    @computed get total() {
        return this.price * this.amount;
    }
}

computed

计算属性可以根据现有的状态计算出新值,任何state的变化影响了最后计算的结果,都会触发相应的observer。

计算属性是被优化过的,如果使用的数据没被更改,它不会重新运行。

action

action是用来修改State的,只执行查找,过滤器等函数不应该被标记为action。

推荐使用严格模式mobx.useStrict(true),这样对于任何不适用动作的状态修改,mobx都会抛出异常。

核心函数autorun

使用 autorun 时,所提供的函数总是立即被触发一次,然后每次它的依赖关系改变时会再次被触发。

先看个最简单的例子

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

autorun(() => {
    console.log(obj.a)
})
// 立即触发一次,输出 :1

obj.b = 3 // 什么都没有发生
obj.a = 2 // observe 函数的回调触发了,控制台输出:2

因为只有obj的a属性在autorun里被用到了,所有只有对它的改动才会触发回调。

Mobx基本数据流

Mobx数据流 可以看到Actions触发State的改变,接着会更新相应的计算属性,最后执行用到该State或者计算属性的方法,这就形成了完整的单向数据流。

与React的结合使用(mobx-react)

observer 函数可以用来将 React 组件转变成响应式组件

它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。 observer 是由单独的 mobx-react 包提供的。

以下实例通过在React class引入@observable, 使得本地状态secondesPassed会每隔1秒触发对应的render, 这样就不需要用setState来管理状态了。 codepen地址:React-mobx sample

import {observer} from "mobx-react"
import {observable} from "mobx"

@observer 
class Timer extends React.Component {
    @observable secondsPassed = 0

    componentWillMount() {
        setInterval(() => {
            this.secondsPassed++
        }, 1000)
    }

    render() {
        return (<span>Seconds passed: { this.secondsPassed } </span> )
    }
})

React.render(<Timer />, document.body)

注意:当 observer 需要组合其它装饰器或高阶组件时,请确保 observer 是最深处(第一个应用)的装饰器。

使用 inject 将组件连接到提供的 stores

mobx-react 包还提供了 Provider 组件, 类似于Redux的Provider,它可以透传sotres。

具体可参考视频 Provider注入stores

componentWillReact

mobx-react 定义了一个新的生命周期钩子函数componentWillReact

当组件因为它观察的数据发生了改变,它会安排重新渲染,这个时候componentWillReact会被触发

总结与进阶

  • Mobx能够追踪观察属性,并对其作出反应,但我们需要更全面的了解它对什么会反应,为什么我们换一种写法它就没有反应了。具体可以参考Mobx对什么作出反应
  • Mobx与React的结合可以说是天衣无缝,如何来优化React的组件渲染,可以参考优化组件渲染
  • 我们会遇到很多异步操作,比如请求数据,那对于编写异步的action,我们需要注意回调需要变成action,具体详见异步action
  • TodoList实例会帮助我们更好地理解Mobx-React,具体参考Todolist-mobx-react