mobx简介

官方文档-传送

MobX是响应式编程,实现状态的存储和管理。使用MobX将应用变成响应式可归纳为三部曲:

  1. 定义状态并使其可观察
  2. 创建视图以响应状态的变化
  3. 更改状态

在介绍这三个步骤之前,我们先了解下MobX的核心API。

一、MobX核心API

1. Observable

observable是将类属性等进行标记,实现对其的观察。三部曲中的第一曲,就是通过Observable实现的。

observable

创建observable的方式很多,推荐使用ES7的装饰器(@observable),如何开启装饰器,后面将会介绍。
observable的value值的类型参照上图,observable包装后返回对应类型的值,如Observable Object等。

2. Actions

动作,通过action改变state。三部曲中的第一曲通过action创建一个动作。action函数是对传入的function进行一次包装,使得function中的observable对象的变化能够被观察到,从而触发相应的衍生。

action
  • action(name,fn):对fn函数进行包装/装饰,name将被用作调试名。
  • runInAction:工具函数,是创建异步action的一种方式。
    action只会对当前包装/装饰的函数做出反应,而不会对当前运行函数所调用的函数(不包含在当前函数之内)作出反应!这意味着如果 action 中存在 setTimeout、promise 的 then 或 async 语句,并且在回调函数中某些状态改变了,那么这些回调函数也应该包装在 action 中。
    runInAction(f)实际上是action(f)()的语法糖。
  • 强制action:设置强制模式后,action必须添加,否则报错。

3. 衍生

对observable的响应。三部曲中的第二部的实现方式。

衍生

衍生主要分为Reactions和Computed values。其中reactions是自动响应状态变化的副作用,如UI变化、控制台输出、后台服务传送数据。而Computed values(计算值)是自动响应状态变化的值。

  • observer:包裹react组件的高阶组件,在render方法中,使用observable包装/装饰的属性。
  • autorun:当你想创建一个响应式函数,但是该函数永远没有观察者,此时使用autorun。这通常是当你需要从反应式代码桥接到命令式代码的情况,例如打印日志、持久化或者更新UI的代码。
    当使用 autorun 时,所提供的函数总是立即被触发一次,然后每次它的依赖关系改变时会再次被触发。
    经验法则:如果你有一个函数应该自动运行,但不会产生一个新的值,请使用autorun。 其余情况都应该使用 computed。
  • when: 观察并运行给定的 predicate,直到返回true。 一旦返回 true,给定的 effect 就会被执行,然后 autorunner(自动运行程序) 会被清理。 该函数返回一个清理器以提前取消自动运行程序。
  • reaction: autorun 的变种,对于如何追踪 observable 赋予了更细粒度的控制。 它接收两个函数参数,第一个(数据 函数)是用来追踪并返回数据作为第二个函数(效果 函数)的输入。
    在执行 效果 函数时访问的任何 observable 都不会被追踪。
  • computed:计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值,创建的一个衍生属性。
  • MobX响应:并非从observable获取了值,MobX就一定响应,如果是在追踪函数之外和异步回调中使用,MobX将不会做出响应。
    “追踪函数” 是 computed 表达式、observer 组件的 render() 方法和when、reaction 和 autorun 的第一个入参函数。

4. Decorators

装饰器,可使用的装饰器列表

observable.deep
observable.ref
observable.shallow
computed
action
action.bound

可以使用 @decorator 语法来应用这些装饰器。

[官方文档说明]

  • observable.deep: 所有 observable 都使用的默认的装饰器。它可以把任何指定的、非原始数据类型的、非 observable 的值转换成 observable。
  • observable.ref: 禁用自动的 observable 转换,只是创建一个 observable 引用。
  • observable.shallow: 只能与集合组合使用。 将任何分配的集合转换为浅 observable (而不是深 observable)的集合。 换句话说, 集合中的值将不会自动变为 observable。
  • computed: 创建一个衍生属性, 参见 computed
  • action: 创建一个动作, 参见 action
  • action.bound: 创建有范围的动作, 参见 action

5. FLOW

创建异步action的一种方式,这里不介绍了。官网传送

二、 开启装饰器(Decorators)

(1)添加装饰器依赖:babel-plugin-transform-decorators-legacy
(2).babelrc文件添加配置

"plugins": ["transform-decorators-legacy"]

三、三部曲

通过一个小demo,讲述如何使用MobX将应该用变成响应式。

首先必须先添加mobx和mobx-react依赖,装饰器依赖根据用户选择添加。

1. 第一曲

定义状态并使其可观察

新建mobx文件夹,observable的相关.js文件存储在此,如下图所示:

image.png

如何定义状态,并使得其可观察呢?接着看代码:
counter_store.js(计数):

import { observable } from 'mobx'

class CounterStore {
    @observable counter = 0;
    @observable remoteCounter = 0;

    constructor(){}

increment(){
        this.counter++;
    }

    decrement(){
        this.counter--;
    }

    incrementAsync(){
        setTimeout(()=>{
            this.counter++
        },500);
    }
}

const counterStore = new CounterStore;

export default counterStore;

CounterStroe类中,对counter、remoteCounter使用了observable装饰器,即将counter、remoteCounter定义为MobX的状态,并可观察。

虽然increment、decrement、incrementAsync函数前无@action,但是因为没有设置强制action,所以默认被action装饰,都是创建了一个动作。

2. 第二曲

创建视图以响应状态的变化

首先使用observer包裹React组件的高级组件:

@observer
export default class HomeScreen extends Component {
  ...
}

其次,在HomeScreen组件的render方法中,使用被observable转换的值。

import counterStore from '../../mobx/counter_store'

    render() {
        return (
            <View style={styles.container}>
                <Text>Counter Container Test</Text>
                <Text>Clicked:<Text>{`${counterStore.counter}times`}</Text></Text>
                <Text onPress={()=>counterStore.increment()}>| +1 |</Text>
                <Text onPress={()=>counterStore.incrementAsync()}>| +1 Async |</Text>
            </View>
        );
    }
}

3. 第三曲

更改状态

在CounterStore类中,通过action创建了三个动作,分别是increment、decrement、incrementAsync。通过调用action装饰过的函数,实现更改状态的目的。
请看代码,onPress事件中调用函数:

import counterStore from '../../mobx/counter_store'

    render() {
        return (
            <View style={styles.container}>
                <Text>Counter Container Test</Text>
                <Text>Clicked:<Text>{`${counterStore.counter}times`}</Text></Text>

                <Text onPress={()=>counterStore.increment()}>| +1 |</Text>
                <Text onPress={()=>counterStore.incrementAsync()}>| +1 Async |</Text>
            </View>
        );
    }
}

当点击‘+1’时,上面的次数显示会响应的变化。这就是所谓的副作用一种形式。

拓展

如何进行状态的引用呢?

两种方式:

  • 全局注册并注入store实例
  • 直接import引用

全局注册并注入store
我参考的demo:https://juejin.im/post/5a3f06cd6fb9a044fe4693bc
demo中使用了Provider进行全局注册并注入Rootstore实例

const Navigation = () => {
    return (
        <Provider rootStore={store}>
            <Navigator/>
        </Provider>
    )
}

在需要使用的组件中,使用@inject('rootStore') ,我们就可以愉快地使用 this.props.rootStore 来拿到我们想要的数据啦

@inject('rootStore')
@observer
export default class HomeScreen extends Component {
  ...
}

注:@observer也需要添加,不然不会随着rootStore属性变化而重新渲染。

直接import引用
不进行全局注册,而是在需要使用mobx的Rootstore实例的组件中进行引用即可。

import counterStore from '../../mobx/counter_store'

Github项目demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • 本文首发于:CSDN「前端开发者说」公众号。CSDN「前端开发者说」公众号(ID:bigfrontend),专注前...
    RachelQG阅读 4,731评论 2 20
  • 1. 介绍 1.1. 原理 React的render是 状态 转化为树状结构的渲染组件的方法而MobX提供了一种存...
    三月懒驴阅读 12,816评论 1 28
  • Mobx是一个功能强大,上手非常容易的状态管理工具。就连redux的作者也曾经向大家推荐过它,在不少情况下你的确可...
    绯色流火阅读 121,263评论 51 170
  • Mobx解决的问题 传统React使用的数据管理库为Redux。Redux要解决的问题是统一数据流,数据流完全可控...
    光哥很霸气阅读 13,073评论 2 21
  • 杜先生,属猪,白羊座,身高一米八零,皮肤黝黑,肚子微凸,背微驼。这是我家先生的基本资料,也是我们第一次见面我所了解...
    效艳一亩田阅读 272评论 2 4