MobX
MobX 是一种简单、可扩展的状态管理,猪齿鱼框架就是用它进行状态管理。
简单理解
mobx修饰组件就是把这个全局的跟组件内的state做关联,全局的变了,局部的也变了,进而触发局部的render。
安装
- 安装:
npm install mobx --save
; - React 绑定库:
npm install mobx-react --save
; - mobx相关依赖:
npm i babel-plugin-transform-class-properties -D
用来编译类(class)
npm i babel-plugin-transform-decorators-legacy -D
用来编译装饰器
npm install --save-dev @babel/plugin-proposal-decorators
装饰器
webpack.config.js 添加如下配置
rules: [{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env','react'],
plugins: ['transform-decorators-legacy', 'transform-class-properties']
}
}
}]
在react-app项目下支持装饰器的解决方法
在根目录下面新建.babelr文件,写入:
{
"presets": ["@babel/preset-env"],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
常用api
- observable 使用:设置可观察数据:使用mobx的observable方法使定义的状态值可以被观察,
observable 是一种让数据的变化可以被观察的方法,值可以是 JS原始数据类型、引用类型、普通对象、类实例、数组和映射。
import { observable, autorun } from 'mobx';
// JS原始类型(Number/String/Boolean)
const value = observable(0);
const number = observable(100);
autorun(() => {
console.log(value.get());
}); // 当观测到的数据发生变化的时候,如果变化的值处在autorun中,那么autorun就会自动执行。
value.set(1);
value.set(2);
number.set(101); // 依次打印 0 1 2
// 数组、对象类型
const list = observable([1, 2, 4]);
list.push(5)
console.log(list[0], list[1], list[2], list[3]) // 1 2 4 5
const obj = observable({a: '11', b: '22'})
obj.a = "leo";
console.log(obj.a, obj.b) // leo 22
// 映射(Map)类型
const map = observable.map({ key: "value"});
map.set("key", "new value");
console.log(map.has('key')) // true
@observable 使用: 使用装饰器 @observable 来将其转换成可观察的。
@observer export default class PictureShow extends React.Component {}
- 响应可观察数据的变化:
- (@)computed:监听相关状态变化时自动更新的值。
const number = observable(10);
const plus = computed(() => number.get() > 0);
autorun(() => {
console.log(plus.get());
});
number.set(-19); // number的值变化,触发computed,输出false
number.set(-1); // number的值变化,因为-1<0,没有改变plus的值,没有输出
number.set(1); // number的值变化,因为1>0, 输出true
- autorun:修改autorun中任意一个可观察数据即可触发自动运行
import { observable, autorun } from 'mobx'
class Store {
@observable str = 'leo';
@observable num = 123;
}
let store = new Store()
autorun(() => {
console.log(`${store.str}--${store.num}`)
})
// leo--123
computed 与 autorun 区别:
@computed
:用于响应式的产生一个可以被其他 observer 使用的值。
autorun
:不产生新的值,而是达到一个效果(如:打印日志,发起网络请求等命令式的副作用),autorun 默认会执行一次,以获取哪些可观察数据被引用。autorun 的作用是在可观察数据被修改之后,自动去执行依赖可观察数据的行为。
@computed
:如果一个计算值不再被观察了,MobX 可以自动地将其垃圾回收,而 autorun 中的值必须要手动清理才行。
- 修改可观察数据
- action: 修改状态的行为,使用 action 的好处是能将多次修改可观察状态合并成一次,从而减少触发 autorun 或者 reaction 的次数。
import { observable, computed, reaction, action} from 'mobx';
class Store {
@observable string = 'leo';
@observable number = 123;
@action bar(){
this.string = 'pingan'
this.number = 100
}
}
let store = new Store()
reaction(() => [store.string, store.number], arr => {
console.log(arr)
})
store.bar() // ["pingan", 100]
- runInAction(name?, thunk):action只能影响正在运行的函数,而无法影响当前函数调用的异步操作。
在回调中需要使用action进行包裹,这里借用官网给出的例子:
@action createRandomContact() {
this.pendingRequestCount++;
superagent
.get('https://randomuser.me/api/')
.set('Accept', 'application/json')
.end(action("createRandomContact-callback", (error, results) => {
if (error)
console.error(error);
else {
const data = JSON.parse(results.text).results[0];
const contact = new Contact(this, data.dob, data.name, data.login.username, data.picture);
contact.addTag('random-user');
this.contacts.push(contact);
this.pendingRequestCount--;
}
}));
}
在end中触发的回调函数,被action给包裹了,action无法影响当前函数调用的异步操作,而回调函数是一个异步操作,所以必须再用一个action来包裹住它,这样程序才不会报错。
如果使用async function来处理,可以使用runInAction这个API来解决之前的问题。
import {observable, action, useStrict, runInAction} from 'mobx';
useStrict(true);
class Store {
@observable name = '';
@action load = async () => {
const data = await getData();
runInAction(() => {
this.name = data.name;
});
}
}
调用load之后,runInAction可以立刻被执行。
结合react使用
import React from 'react';
import { observable, useStrict, action } from 'mobx';
import { observer } from 'mobx-react';
useStrict(true);
class MyState {
@observable num = 0;
@action addNum = () => {
this.num++;
};
}
const newState = new MyState();
@observer
export default class App extends React.Component {
render() {
return (
{newState.num}
)
}
}
这里定义了一个类MyState,包括可以被观察的num变量和action函数来改变num,实例化这个类并在组件中使用,使用@observer修饰App组件,组件中可以改变和观察num的值。
跨组件交互
在不使用状态管理的React要实现跨组件交互,通常需要我们在父组件定义state和修改state的函数,然后再通过props传给不同的组件,这样看起来逻辑简单,但在业务很复杂的情况下就会很繁琐,而Mobx可以更加简单的解决问题。
class MyState {
@observable num1 = 0;
@observable num2 = 100;
@action addNum1 = () => {
this.num1 ++;
};
@action addNum2 = () => {
this.num2 ++;
};
@computed get total() {
return this.num1 + this.num2;
}
}
const newState = new MyState();
const AllNum = observer((props) => num1 + num2 = {props.store.total});
const Main = observer((props) => (
num1 = {props.store.num1}
num2 = {props.store.num2}
));
@observer
export default class App extends React.Component {
render() {
return (
);
}
}
上面的一段代码很好的解释了跨组件交互:Main和AllNum是两个不相关的组件,在MyState中存放组件共同需要的状态和函数,通过props将状态和函数传给两个子组件,子组件可以调用方法改变相应的状态的值。