mobx是一个简单可扩展的状态管理库
基本概念
- state(状态) 状态是驱动应用的数据,像有数据的excel表格
2.derivations(衍生) 任何源自状态并且不会再进一步相互作用的东西。比如用户界面,待办事件的数量,把变化发送到服务端
两种类型的衍生
(1)computed values 是可以使用pure function从state中推导的值,mobx可以自动更新,并且在不在使用时将其优化掉,比如
@computed get completedTodos(){
return this.todos.filter(todo => todo.done)
}
(2)reactions很像computed values,会对state的变化做出反应,例如更新UI
const Todos = observer({todos} =>
<ul>
todos.map( todo =>
<li>{todo.title}</li>
)
</ul>
)
3.actions动作 动作 是任一一段可以改变状态的代码。用户事件、后端数据推送、预定事件、等等。 动作类似于用户在excel单元格中输入一个新的值。
actions可以改变state,并且是唯一改变state的东西
原则
MobX 支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图。
当状态改变时,所有衍生都会进行原子级的自动更新,不能观测到中间值。
所有衍生默认的都是同步更新。动作可以在改变状态之后直接安全的检查计算值???
计算值是延迟更新的,不使用的计算值不更新,直到使用时。
所有的计算值都是纯净的,不应该用来改变state
mobx工作流程
事件调用actions,actions可以改变state,并且唯一能改变state的就是action
@action onClick = () => {
this.props.todo.done = true;
}
state是可观察的,并且不包含冗余的数据
@observable todos = [{
title: 'learn mobx',
done: false
}]
computed values 是可以使用pure function从state中推导的值,mobx可以自动更新,并且在不在使用时将其优化掉
@computed get completedTodos(){
return this.todos.filter(todo => todo.done)
}
reactions很像computed values,会对state的变化做出反应,例如更新UI
const Todos = observer({todos} =>
<ul>
todos.map( todo =>
<li>{todo.title}</li>
)
</ul>
)
一个例子
1.定义状态并使其可观察
所有改变的属性打上,mobx的标记使其变得可观察。
import {observable} from 'mobx';
var appState = observable({
timer:0
});
2.创建视图以响应状态的变化
创建视图,在appState中相关数据发生改变时视图会自动更新。
import {observer} from 'mobx-react';
@observer
class TimerView extends React.Component{
render(){
return (<button onClick={this.onReset.bind(this)}>
Seconds passed: {this.props.appState.timer}</button>);
}
onReset () {
this.props.appState.resetTimer();
}
};
ReactDOM.render(<TimerView appState={appState} />,document.body);
定义一个按钮,显示timer,当点击这个按钮时,调用reset函数,重置时间。
3.更改状态
每秒都修改数据,当需要的时候UI自动更新
appState.resetTimer = action(function reset(){
appState.timer = 0;
});
setInterval(action(function tick(){
appState.timer +=1;
}),1000);
API
1.observable 用法:
--observable(value)
--@observable classProperty = value
Observable 值可以是JS基本数据类型、引用类型、普通对象、类实例、数组和映射
const list = observable([1, 2, 4]);
list[2] = 3;
@observable装饰器可以在 ES7 或者 TypeScript 类属性中属性使用,将其转换成可观察的。
import { observable, computed } from "mobx";
class OrderLine {
@observable price = 0;
@observable amount = 1;
@computed get total() {
return this.price * this.amount;
}
}
2.computed
计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值。计算值还是高度优化过的,所以尽可能的多使用它们。
import {observable, computed} from "mobx";
class OrderLine {
@observable price = 0;
@observable amount = 1;
constructor(price) {
this.price = price;
}
@computed get total() {
return this.price * this.amount;
}
}
计算值的 setter
还可以为计算值定义 setter。注意这些 setters 不能用来直接改变计算属性的值,但是它们可以用来作“逆向”衍生。
注意永远在 getter 之后 定义 setter
class Foo {
@observable length = 2;
@computed get squared() {
return this.length * this.length;
}
set squared(value) { // 这是一个自动的动作,不需要注解
this.length = Math.sqrt(value);
}
}
(暂时没有用到过,不知道用来干什么需要使用)
3.autorun
当你需要从反应式代码桥接到命令式代码的情况,例如打印日志、持久化或者更新UI的代码,应该使用autorun
当使用 autorun 时,所提供的函数总是立即被触发一次,然后每次它的依赖关系改变时会再次被触发
var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0));
var disposer = autorun(() => console.log(sum.get()));
// 输出 '6'
numbers.push(4);
// 输出 '10'
- action
动作是任何用来修改状态的东西
action.bound 来自动地将动作绑定到目标对象
class Ticker {
@observable tick = 0
@action.bound
increment() {
this.tick++ // 'this' 永远都是正确的
}
}
const ticker = new Ticker()
setInterval(ticker.increment, 1000)
注意: action.bound 不要和箭头函数一起使用;箭头函数已经是绑定过的并且不能重新绑定。
runInAction
runInAction 是个简单的工具函数,它接收代码块并在(异步的)动作中执行。这对于即时创建和执行动作非常有用,例如在异步过程中。
如果你使用async function来处理业务,需要使用runInAction。
computed 和 autorun
它们都是响应式调用的表达式,但是,如果你想响应式的产生一个可以被其它 observer 使用的值,请使用 @computed.
使用 autorun达到某种效果,比如像打印日志、发起网络请求等