MobX的一些总结

MobX

MobX通过透明的函数响应式编程,使得其成为一种简单、可扩展的状态管理工具。使用MobX可以将应用编程响应式的编程方式。

其背后的原理是:任何源自应用状态的东⻄都应该自动地获得。

React和MobX是一对强力组合。React通过提高机制把应用状态转换为可渲染组件树并对其进行渲染;MobX提供机制来存储和更新应用状态供React使用。

1. 概念

MobX Overview

MobX主要区分了概念:

  • State(状态)
  • Derivations(衍生)
  • Actions(动作)。

在Derivations中又可以再细分为Computed values(计算值)与Reactions(反应)。

  1. State(状态)

    状态是驱动应用的数据。这其中包含对象,数组等。

  2. Derivations(衍生)

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

    衍生以多种形式存在:

    • 用户界面
    • 衍生数据,比如剩下的待办事项的数量。
    • 后端集成,比如把变化发送到服务器端。

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

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

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

  3. Actions(动作)

    动作是任一一段可以改变状态的代码。例如用户事件、后端数据推送、预定事件、等等。

    在MobX中可以显式地定义动作,它可以帮你把代码组织的更清晰。如果是在严格模式下使用MobX的话,MobX会强制只有在动作之中才可以修改状态。

2. 原则

MobX支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图

State

  • State(状态)改变时,所有Derivations(衍生)都会进行原子级的自动更新。因此永远不可能观察到中间值。
  • 所有Derivations(衍生)默认都是同步更新。这意味着例如Actions(动作)可以在改变State(状态)之后直接可以安全地检查计算值。
  • Computed values(计算值)是延迟更新的。任何不在使用State(状态)的 Computed values(计算值)将不会更新,直到需要它进行副作用(I / O)操作时。 如果视图不再使用,那么它会自动被垃圾回收。
  • 所有的 Computed values(计算值)都应该是纯净的。它们不应该用来改变State(状态)。

3. API

装饰器

装饰器说明
@observable可以在 ES7 或者 TypeScript 类属性中属性使用,将其转换成可观察的。 @observable 可以在实例字段和属性 getter 上使用。
@action用于装饰可观察变量的状态的更新。在严格模式下,对于可观察变量的状态的更新必须使用此装饰器
@observer用来将 React 组件转变成响应式组件。它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。
@computed如果已经启用 decorators 的话,可以在任意类属性的 getter 上使用 @computed 装饰器来声明式的创建计算属性。

其他

  1. Autorun

    当你想创建一个响应式函数,而该函数本身永远不会有观察者时,可以使用mobx.autorun

    这通常是当你需要从反应式代码桥接到命令式代码的情况,例如打印日志、持久化或者更新UI的代码。

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

  2. Reaction

    它是autorun的变种,对于如何追踪observable赋予了更细粒度的控制。

    它接收两个函数参数,第一个(数据 函数)是用来追踪并返回数据作为第二个函数(效果 函数)的输入。

    不同于 autorun 的是当创建时效果 函数不会直接运行,只有在数据表达式首次返回一个新值后才会运行。 在执行 效果 函数时访问的任何 observable 都不会被追踪。

    reaction返回一个清理函数。

    用法:

    reaction(() => data, (data, reaction) => { sideEffect }, options?)
    
  3. enforceActions(严格模式)

    在严格模式下,不允许在 action 外更改任何状态。 可接收的值:

    • "never" (默认): 可以在任意地方修改状态
    • "observed": 在某处观察到的所有状态都需要通过动作进行更改。在正式应用中推荐此严格模式。
    • "always": 状态始终需要通过动作来更新(实际上还包括创建)。
  4. runInAction: 异步 Actions

    在严格模式下,所有修改可观察变量的地方必须(放在action中或)添加@action。但@action只会对当前运行的函数作出反应,而不会对当前运行函数所调用的函数(不包含在当前函数之内)作出反应。这意味着如果@action中存在setTimeoutpromisethenasync语句,并且在回调函数中某些状态改变了,那么这些回调函数也应该包装在@action中。这时候我们可以使用runInAction包装修改可观察变量。

    mobx.configure({ enforceActions: "always" });
    
    class Store {
        @observable githubProjects = []
        @observable state = "pending" // "pending" / "done" / "error"
    
        @action
        fetchProjects() {
            this.githubProjects = []
            this.state = "pending"
            fetchGithubProjectsSomehow().then(
                projects => {
                    const filteredProjects = somePreprocessing(projects)
                    // 将‘“最终的”修改放入一个异步动作中
                    runInAction(() => {
                        this.githubProjects = filteredProjects
                        this.state = "done"
                    })
                },
                error => {
                    // 过程的另一个结局:...
                    runInAction(() => {
                        this.state = "error"
                    })
                }
            )
        }
    }
    
    

    async / await只是围绕基于promise过程的语法糖。 结果是@action仅应用于代码块,直到第一个 await。 在每个await之后,一个新的异步函数将启动,所以在每个await之后,状态修改代码应该被包装成动作。 这正是runInAction再次派上用场的地方。

    mobx.configure({ enforceActions: "always" });
    
    class Store {
        @observable githubProjects = []
        @observable state = "pending" // "pending" / "done" / "error"
    
        @action
        async fetchProjects() {
            this.githubProjects = []
            this.state = "pending"
            try {
                const projects = await fetchGithubProjectsSomehow()
                const filteredProjects = somePreprocessing(projects)
                // await 之后,再次修改状态需要动作:
                runInAction(() => {
                    this.state = "done"
                    this.githubProjects = filteredProjects
                })
            } catch (error) {
                runInAction(() => {
                    this.state = "error"
                })
            }
        }
    }
    

对比及总结

  1. autorun需要手动清理,@computed会被自动清理。
  2. autorun总是立即被触发一次,@computed只有当它有自己的观察者时才会重新计算。
  3. autorun用于执行打印日志、持久化或者更新UI的代码,@computed用于产生一个新值。
  4. 如果你有一个函数应该自动运行,但不会产生一个新的值,请使用autorun。 其余情况都应该使用@computed

4. 使用

安装MobX

yarn add mobx --save
yarn add mobx-react --save

MobX推荐使用ES7的decorator语法,以实现最精炼的表达。安装装饰器支持:

npm i --save-dev babel-plugin-transform-decorators-legacy

然后在 .babelrc 文件中启用它:

{
  "presets": ["module:metro-react-native-babel-preset"],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
    [
      "@babel/transform-runtime",
      {
        "helpers": true,
        "regenerator": false
      }
    ]
  ]
}

一些问题

最新的5.x以上的mobx在安装时存在问题,这里我自己使用的是4.x

另外对于Babel也需要使用

贴出自己的package.json

"dependencies": {
    "babel-core": "^6.26.3",
    "babel-preset-stage-0": "^6.24.1",
    "mobx": "^4.3.1",
    "mobx-react": "^5.1.0",
    "react": "16.6.1",
    "react-native": "0.57.7"
  },
  "devDependencies": {
    "@babel/core": "^7.2.0",
    "@babel/plugin-proposal-decorators": "^7.2.0",
    "@babel/plugin-transform-runtime": "^7.2.0",
    "@babel/runtime": "^7.2.0",
    "babel-jest": "23.6.0",
    "babel-plugin-transform-decorators-legacy": "^1.3.5",
    "babel-preset-react-native-stage-0": "^1.0.1",
    "jest": "23.6.0",
    "metro-react-native-babel-preset": "0.50.0",
    "react-test-renderer": "16.6.1"
  }

参考

  1. Ten minute introduction to MobX and React
  2. MobX CN
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值