mobx是啥:是一个用来管理状态的库,如果被观测组件发生改变,会自动渲染有关页面,告别setState。
mbox编程的3个重点:
Actions:一段可以改变观测变量状态的代码,类似于setState,严格模式下,只有在动作中才可以修改状态
mobx编程的4个概念:
计算值和反应的区别:计算值会产生新的可观测量,反应为所有变量包括可观测变量的一系列运算,并不产生新的可观测量
computed value可以看作一个包含各种计算的变量,计算属性本质是方法,只是在使用这些计算属性的时候,把他们的名称直接当作属性来使用,并不会把计算属性当作方法去调用,不需要加小括号()调用。
此计算属性的方法内部所用到的任何data中的数据,依赖响应属性只要发生改变,就会立即重新计算,即触发这个计算属性的重新求值;否则不会重新计算求值。
计算属性的求值结果会被缓存起来,方便下次直接使用(多次调用只要内部数据不改变就不会重新求值,改变了也只会计算一次,虽然有多个地方引用此属性)。
计算属性的方法内部无论如何都要return出去一个值。
若使用create-react-app工具创建的工程,首先需要npm run eject,然后在package.json中输入:
"babel": {
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
],
"presets": [
"react-app"
]
}
以支持装饰器语法。
最后需要导入的包:mobx和mobx-react,可以使用npm install mobx,mobx-react --save
来导入
import React from 'react';
import ReactDOM from 'react-dom'
import {
observable,action} from 'mobx';
import {
observer} from 'mobx-react';
var appState = observable({
timer: 0
});
appState.resetTimer = action(function reset() {
appState.timer = 0;
});
setInterval(action(function tick() {
appState.timer += 1;
}), 1000);
@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);
类型:Array/Object/Map:
类型:其他需要使用box封装:
注意:只会对声明observable时的已有属性进行观察,新加入的属性不会监视,需要用extendObservable()来处理
建议的最佳实践:在程序初始化时就声明出所有会被使用的属性
修饰器只能修饰类和类属性,修饰器统一使用@observable来修饰,无需使用box
如:
class Store{
@observable array=[];
@observable obj={
};
@observable map=new Map();
@observable string='abc';
@observable number=20;
@observable bool=false;
}
注意:获取和赋值都与正常一样,但是在取值时取得值是mobx的observable类型,可以使用mobx提供的toJS()
方法来将该类型转换为普通的js类型。
前提:
class Store{
@observable string='a';
@observable num=12;
}
var store=new Store();
computed:组合观察数据形成一个新观测值,接受一个无参数的函数,必须有返回值,且使用get()方法来获取计算值
使用函数:
// 注入store
// 定义foo变量,表示string和num的计算值
var foo=computed(function(){
return store.string+'/'+store.num});
// 当foo变化时调用指定的函数执行操作 foo.observe(函数)
foo.observe(function(change){
console.log(change);}) //可以用来观察监测值,如果有改动就调用函数
使用注解:
// 若使用注解,则必须在store类中事先定义好计算值
@computed
get mixed(){
return store.string+'/'+store.num}};
// 调用autorun方法,接受一个函数,当函数内部的可观测数据变化时执行函数,此处由于没有使用mixed计算值,则string或者num变化都会输出
autorun(()=>{
console.log(return store.string+'/'+store.num);});
// 使用mixed计算值,只有该值变化才输出
autorun(()=>{
console.log(store.mixed);})
autorun中的可观察数据如果发生改变,则自动运行一次,注意:第一次定义autorun也会运行一次,接收一个无参函数
接收两个函数作为参数,当第一个函数返回true,就会执行一次第二个函数,且全局只执行一次,不再被触发
when(()=>stroe.bool,()=>console.log('it is true'));
注意:第一个函数必须根据可观察数据来计算返回值,不能是普通不可观察变量
接收两个函数作为参数,第一个函数中的可观察值变化时执行第二个函数,相当于when的循环版
该装饰器保证在其内部修改的变量会等待函数运行结束后再同时触发反应。使用方式:
action("xxx",()=>{
…})
action 包装/装饰器只会对当前运行的函数作出反应,而不会对当前运行函数所调用的函数(不包含在当前函数之内)作出反应!
这意味着如果 action 中存在 setTimeout、promise 的 then 或 async 语句,并且在回调函数中某些状态改变了,那么这些回调函数也应该包装在 action 中。可以使用runInAction解决。
@action:修饰的函数必须注意,如果其内部调用了非**@action.bound修饰的异步回调函数,会报错,因为回调函数不在当前上下文环境中,导致this指向错误!可以使用action或者runInAction**包裹代码来解决。
@action.bound:该装饰器将函数上下文绑定了,可以放在其他上下文中依然正确执行,这种写法在将方法传给callback时非常有用
var run = dStore.run
run()
随时定义匿名的action然后运行,接收一个无参函数
注意:在异步或者回调函数中,@action范围在仅仅在代码块,直到第一个await,如果后续还有其他的await,需要使用runInAction包裹!
属于mobx-react包,与react结合使用,在会调用函数导致改变mobx状态的react组件上注解
属于mobx-react包,如果当前react项目需要启用mobx管理状态,则必须在根节点上使用Provider标签包裹,同时传递注入对象
在store.js中汇总所有待管理store
import test from './test'
import mast from './mast'
const stores = {
test,
mast,
}
export default stores
在App.jsx中
import {
Provider } from "mobx-react"
import stores from './store'
import {
configure} from 'mobx'; // 开启严格模式
configure({
enforceActions: true}) // 开启严格模式
class App extends Component{
render(){
return(
<Provider store={
...store}>
<ToDoApp/>
</Provider>
)
}
}
configure
代表开启了严格模式,因为非严格模式下,组件是直接可以通过props.action
改变state数据的,当项目复杂的时候这样是非常危险的。所以要设置唯一改变state方式是通过action
属于mobx-react包,对当前的react组件进行注入store,以便访问可观察值,注入的值在this.props
中
@inject('todoList')
class ToDoApp extends Component{
render(){
return (
<div>
<TodoListView todoList={
this.props.todoList}/>
</div>
);
}
}
import {
observable, autorun, computed, when ,reaction, action, runInAction} from 'mobx';
class DemoStore{
@observable age = 0;
@observable name = 'pp';
@observable number = 0;
@computed get lucky(){
return this.age+this.number;
}
@action run(){
this.age=111;
this.name='gaga';
this.number=222;
}
@action.bound runOut(){
this.age=222;
this.name='jjj';
this.number=this.age+this.number;
}
}
var dStore = new DemoStore();
autorun(()=>{
console.log("autorun:"+dStore.lucky)
})
when(()=>dStore.age>18,()=>{
console.log("when:你可以看了"+dStore.age);})
reaction(()=>[dStore.age,dStore.name],()=>console.log("reaction:age+name="+dStore.age+dStore.name))
dStore.name='abc';
dStore.number=20;
dStore.age=20;
dStore.run()
var runOut=dStore.runOut;
runOut();
runOut();
runInAction('abc',()=>{
dStore.age=9;
dStore.name='qqq';
dStore.number=6;
})