Littlest things

Mobx使用之TodoList

Mobx

Simple, scalable state management –简单、可扩展的状态管理。

It is inspired by MVVM frameworks like in MeteorJS tracker, knockout and Vue.js.

Mobx Install

1
2
3
4
5
6
7
npm install --save mobx mobx-react
npm install --save-dev babel-plugin-transform-decorators-legacy
.babelrc
"plugins": [
"transform-decorators-legacy"
]

Mobx是通过观察者模式实现的,相对Redux他写起了让人更加的容易理解和轻松。

几个主要的Api

如果使用过Vue那么下面几个Api非常容易理解。

observable

observable 是Mobx中最重要的Api。作用是声明一个值被订阅。
Usage:

  • observable(value)
  • @observable classProperty = value(如果使用装饰器,需要使用babel的插件,先问将介绍)
    value的值可以是JavaScript的基础类型,也可以是Object、Array、Map
    如果value是map,则回返回一个Observable Map类型的变量
    如果value是array,则回返回一个Observable Array 类型的变量
    如果value是Object,则回返回一个Observable Object类型的变量

observer

如果要在React中使用mobx,需要对组件使用observer

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
import {observer} from 'mobx-react' // for react
import {observer} from 'mobx-react/native' // for react-native
//无状态组件, ES5:
const Timer = observer(function(props) {
return React.createElement("div", {}, props.timer.elapsedTime)
})
//无状态组件, ES6:
const Timer = observer(({ timer }) =>
<div>{ timer.elapsedTime }</div>
)
//React 组件, ES5:
const Timer = observer(React.createClass({
}))
//React 组件类, ES6:
const Timer = observer(class Timer extends React.Component {
/* ... */
})
//React 组件类与装饰器, ES.next:
@observer
class Timer extends React.Component {
/* ... */
}

autorun

observable(value)说的到的值发生了change,如果autorun函数订阅了这个值,则autorun这个函数会被执行。

  • 使用console.log时是非常好用的。
  • 如果监听一个值实时提交数据,autorun也是非常好用的。但是这种情况我更加推荐使用autorunAsync
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var number = observable(1);
    autorun(()=>console.log(number.get()))
    // Print 1 第一次是因为初始化
    number.set(0)
    // Print 0
    number.set(2)
    // Print 2
    number.set(3)
    // Print 3
    number.set(3)
    // Print Nothing

computed

返回通过计算得出的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var person = observable({
firstName:"Hehe",
lastName:"Haha"
});
var fullName = computed(() =>{
return `${person.firstName}-${person.lastName}`
});
autorun(()=>{
console.log(fullName.get())
})
person.firstName = 12313
//Print Hehe-haha
//Print 12313-haha

TodoList实战

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import React from 'react';
import ReactDOM from 'react-dom';
import {observable, autorun, computed, action} from 'mobx'
import {observer} from 'mobx-react'
class Todo {
id = null
@observable
content = ""
@observable
completed = false
constructor(content) {
this.id = Date.now();
this.content = content
autorun(() => {
console.log(`${this.content} change to ${this.completed}`)
})
}
}
class TodoListStore {
@observable
todoList = [new Todo('studying react'),
new Todo('studying vue')]
constructor() {
autorun(() => {
console.log(`todo的总数目发生了变化${this.count}`)
})
autorun(() => {
console.log(`todo的已完成总数目发生了变化${this.completedCount}`)
})
}
@computed get count() {
return this.todoList.length
}
@computed get completedCount() {
return this.todoList.filter(value => {
return value.completed == true
}).length
}
@action
addItem(content) {
this.todoList.push(new Todo(content))
}
}
let todoList = new TodoListStore()
@observer
class TodoItem extends React.Component {
static propTypes = {}
constructor(props) {
super(props)
}
toggle(){
this.props.data.completed = !this.props.data.completed
}
render() {
return (
<li onClick={::this.toggle}>
{
this.props.data.completed? <s>{this.props.data.content}</s>:this.props.data.content
}
</li>
)
}
}
@observer
class TodoList extends React.Component {
static propTypes = {}
constructor(props) {
super(props)
}
addItem(){
this.props.data.addItem(this.refs.input.value)
}
render() {
return (
<div>
<ul>
{
this.props.data.todoList.map((value)=>{
return (
<TodoItem data={value} key={value.id}/>
)
})
}
</ul>
{this.props.data.completedCount}/{this.props.data.count}
<p><input ref="input" type="text"/> <button onClick={::this.addItem}>add</button></p>
</div>
)
}
}
ReactDOM.render(<TodoList data={todoList}/>, document.getElementById('app'));

如果不用Mobx直接中setState,而改变completed属性的方法,需要类似于forEach这样的方法。也涉及到子组件向父组件的通讯。
将state的值抽象出来,封装成类。
在这里抽象出了两个类: TodoListStore Todo,通过类中的方法来进行状态的改变
可以看到这整段代码的执行过程中没有一个setState操作。