MobX
MobX 是一種簡單、可擴展的狀態(tài)管理,豬齒魚框架就是用它進行狀態(tài)管理。
簡單理解
mobx修飾組件就是把這個全局的跟組件內的state做關聯(lián),全局的變了,局部的也變了,進而觸發(fā)局部的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 使用:設置可觀察數(shù)據:使用mobx的observable方法使定義的狀態(tài)值可以被觀察,
observable 是一種讓數(shù)據的變化可以被觀察的方法,值可以是 JS原始數(shù)據類型、引用類型、普通對象、類實例、數(shù)組和映射。
import { observable, autorun } from 'mobx';
// JS原始類型(Number/String/Boolean)
const value = observable(0);
const number = observable(100);
autorun(() => {
console.log(value.get());
}); // 當觀測到的數(shù)據發(fā)生變化的時候,如果變化的值處在autorun中,那么autorun就會自動執(zhí)行。
value.set(1);
value.set(2);
number.set(101); // 依次打印 0 1 2
// 數(shù)組、對象類型
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 {}
- 響應可觀察數(shù)據的變化:
- (@)computed:監(jiān)聽相關狀態(tài)變化時自動更新的值。
const number = observable(10);
const plus = computed(() => number.get() > 0);
autorun(() => {
console.log(plus.get());
});
number.set(-19); // number的值變化,觸發(fā)computed,輸出false
number.set(-1); // number的值變化,因為-1<0,沒有改變plus的值,沒有輸出
number.set(1); // number的值變化,因為1>0, 輸出true
- autorun:修改autorun中任意一個可觀察數(shù)據即可觸發(fā)自動運行
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 區(qū)別:
@computed:用于響應式的產生一個可以被其他 observer 使用的值。
autorun:不產生新的值,而是達到一個效果(如:打印日志,發(fā)起網絡請求等命令式的副作用),autorun 默認會執(zhí)行一次,以獲取哪些可觀察數(shù)據被引用。autorun 的作用是在可觀察數(shù)據被修改之后,自動去執(zhí)行依賴可觀察數(shù)據的行為。
@computed:如果一個計算值不再被觀察了,MobX 可以自動地將其垃圾回收,而 autorun 中的值必須要手動清理才行。
- 修改可觀察數(shù)據
- action: 修改狀態(tài)的行為,使用 action 的好處是能將多次修改可觀察狀態(tài)合并成一次,從而減少觸發(fā) autorun 或者 reaction 的次數(shù)。
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只能影響正在運行的函數(shù),而無法影響當前函數(shù)調用的異步操作。
在回調中需要使用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中觸發(fā)的回調函數(shù),被action給包裹了,action無法影響當前函數(shù)調用的異步操作,而回調函數(shù)是一個異步操作,所以必須再用一個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可以立刻被執(zhí)行。
結合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 (
<div>
<p>{newState.num}</p>
<button onClick={newState.addNum}>+1</button>
</div>
)
}
}
這里定義了一個類MyState,包括可以被觀察的num變量和action函數(shù)來改變num,實例化這個類并在組件中使用,使用@observer修飾App組件,組件中可以改變和觀察num的值。
跨組件交互
在不使用狀態(tài)管理的React要實現(xiàn)跨組件交互,通常需要我們在父組件定義state和修改state的函數(shù),然后再通過props傳給不同的組件,這樣看起來邏輯簡單,但在業(yè)務很復雜的情況下就會很繁瑣,而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) => <div>num1 + num2 = {props.store.total}</div>);
const Main = observer((props) => (
<div>
<p>num1 = {props.store.num1}</p>
<p>num2 = {props.store.num2}</p>
<div>
<button onClick={props.store.addNum1}>num1 + 1</button>
<button onClick={props.store.addNum2}>num2 + 1</button>
</div>
</div>
));
@observer
export default class App extends React.Component {
render() {
return (
<div>
<Main store={newState} />
<AllNum store={newState} />
</div>
);
}
}
上面的一段代碼很好的解釋了跨組件交互:Main和AllNum是兩個不相關的組件,在MyState中存放組件共同需要的狀態(tài)和函數(shù),通過props將狀態(tài)和函數(shù)傳給兩個子組件,子組件可以調用方法改變相應的狀態(tài)的值。