mobx

一、简介

​ mobx 与 redux,vuex 类似,是状态(state)管理的一种解决方案,与 react 可以很好的搭配使用。

二、概念

mobx 的工作流程。

flow

@observable

1
@observable tempStr = "";

将某个值更改为可观察状态,observable 的属性值在其变化的时候 mobx 会自动追踪并作出响应。

当定义好其 observable 的对象值后,对象中后来添加的值是不会变为可观察的,这时需要使用 extendObservable 来扩展对象。

@action

1
2
3
4
@action
handleInputChange = e => {
this.newTodoTitle = e.target.value;
};

可以通过 action 修改 state,并且可以有副作用(异步)。action 之间可以互相调用。在严格模式下 state 只能由 action 修改。

@computed

1
2
3
4
@computed
get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length;
}

计算属性值实际上是一类衍生值,它是根据现有的状态或者其他值计算而来,原则上在计算属性中尽可能地不对当前状态做任何修改,同时对于任何可以通过现有状态数据得到的值都应该通过计算属性获取。

类似于 vue 中的 computed。

autorun

1
2
3
4
5
6
7
8
9
10
@observable number = 0;

@action
change (i) {
this.number += 1;
}

autorun(() => {
console.log(this.number);
});

observable 可以用来观测一个数据,这个数据可以数字、字符串、数组、对象等类型(相关知识点具体会在后文中详述),而当观测到的数据发生变化的时候,如果变化的值处在autorun中,那么autorun就会自动执行。

类似于 vue 对单个 data 进行 watch 监听。

@observer

1
2
3
4
5
import { observer } from "mobx-react";

@observer
class CounterPage extends Component {
}

observer 将 React 组件转化成响应式组件,它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件,视图可以即时响应。

三、基本使用

以在 react 中写一个计数器为例

1
npm i mobx mobx-react

3.1 入口文件

src/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from "react";
import ReactDOM from "react-dom";
import App from "./router/index.js";
import * as serviceWorker from "./serviceWorker";
import { Provider } from "mobx-react";
import stores from "./models/stores.js";

ReactDOM.render(
<Provider {...stores}>
<App />
</Provider>,
document.getElementById("root")
);

serviceWorker.unregister();

使用 mobx 提示的 provider 组件来包裹最外层组件节点,并传入多个store 的集合,其后代组件就可以拿到所需的 store。这里利用 React context 机制。

3.2 编写 store

mobx 是有多个 store,可以用 class 的方式定义 一个store,哪个组件需要用到,就将 store 通过 props 传递过去,传递层级过高时可以使用 context 传递。

src/models/counter.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { observable, action } from "mobx";

class Counter {
@observable number = 0;

@action
increase(title) {
this.number += 1;
}

@action
decrement() {
this.number -= 1;
}

@action
asyncIncrease() {
new Promise(resolve => {
setTimeout(() => {
resolve(2);
}, 2000);
}).then(data => {
this.number += data;
});
}
}

export default Counter;

统一导出为 stores

src/models/stores.js

1
2
3
4
5
6
7
import Counter from "./counter.js";

const stores = {
counter: new Counter()
};

export default stores;

3.3 组件中使用 store

src/containers/counter/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { Component } from "react";
import { inject, observer } from "mobx-react";

@inject("counter")
@observer
class CounterPage extends Component {
onClickIncrease = () => {
this.props.counter.increase();
};

onClickDecrement = () => {
this.props.counter.decrement();
};

onClickAsyncIncrease = () => {
this.props.counter.asyncIncrease();
};

render() {
return (
<div>
<p>
counter number: <span>{this.props.counter.number}</span>
</p>
<button onClick={this.onClickIncrease}>increase</button>
<button onClick={this.onClickDecrement}>decrement</button>
<button onClick={this.onClickAsyncIncrease}>async decrement</button>
</div>
);
}
}

export default CounterPage;

​ 使用 @inject 给组件注入其需要的 store,通过 @observer 将 React 组件转化成响应式组件,它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。

3.4 严格模式

mobx 3.0

1
2
3
import { configure } from "mobx";

configure({ enforceActions: 'always' });

​ 在 mobx 中,可以有很多种方式去修改 state,mobx 并不对其做限制,但是如果使用了严格模式,那么将会限制开发者只能通过 @action 来修改 state,这将会更有利于组织代码以及使数据流更清晰。

​ 实际开发的时候建议开起严格模式,这样不至于让你在各个地方很轻易地区改变你所需要的值,降低不确定性。

四、调试

在开发的过程中,为了更加方便地实时查看到 state 状态,可以使用 mobx-react-devtools

调试插件

1
2
3
4
5
6
7
8
9
import DevTools from "mobx-react-devtools";

render(
<div>
<DevTools />
<TodoList store={store} />
</div>,
document.getElementById("root")
);

相关依赖:”mobx”: “^5.7.0”, “mobx-react”: “^5.4.2”

end
本文结束,感谢您的阅读