mobx快速入門
mobx
mobx是啥:是一個用來管理狀態的庫,如果被觀測組件發生改變,會自動渲染有關頁面,告別setState。
mbox編程的3個重點:
- observer觀測器:帶有觀測器的react組件或者屬性被mobx實時觀測
- observable可觀測對象:由mobx建立的可觀測對象
- action更新事件:標識觀測對象的改變事件
Actions:一段可以改變觀測變量狀態的代碼,類似於setState,嚴格模式下,只有在動作中才可以修改狀態
mobx編程的4個概念:
- State:狀態:相當於有數據的表格
- Derivations:驅動
- computed value:計算值
- reactions:反應
計算值和反應的區別:計算值會產生新的可觀測量,反應爲所有變量包括可觀測變量的一系列運算,並不產生新的可觀測量
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
來導入
mobx quickstart
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);
mobx聲明
1. 使用函數封裝:
類型:Array/Object/Map:
- 聲明:var arr = observable([1,2,3])
- 取值:arr[0]
- 賦值:arr[0]=4
類型:其他需要使用box封裝:
- 聲明:var num = observable.box(20)
- 取值:num.get()
- 賦值:num.set(4)
注意:只會對聲明observable時的已有屬性進行觀察,新加入的屬性不會監視,需要用extendObservable()來處理
建議的最佳實踐:在程序初始化時就聲明出所有會被使用的屬性
2. 使用修飾器
修飾器只能修飾類和類屬性,修飾器統一使用@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類型。
computed
前提:
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中的可觀察數據如果發生改變,則自動運行一次,注意:第一次定義autorun也會運行一次,接收一個無參函數
when
接收兩個函數作爲參數,當第一個函數返回true,就會執行一次第二個函數,且全局只執行一次,不再被觸發
when(()=>stroe.bool,()=>console.log('it is true'));
注意:第一個函數必須根據可觀察數據來計算返回值,不能是普通不可觀察變量
reaction
接收兩個函數作爲參數,第一個函數中的可觀察值變化時執行第二個函數,相當於when的循環版
action
該裝飾器保證在其內部修改的變量會等待函數運行結束後再同時觸發反應。使用方式:
action("xxx",()=>{…})
action 包裝/裝飾器只會對當前運行的函數作出反應,而不會對當前運行函數所調用的函數(不包含在當前函數之內)作出反應!
這意味着如果 action 中存在 setTimeout、promise 的 then 或 async 語句,並且在回調函數中某些狀態改變了,那麼這些回調函數也應該包裝在 action 中。可以使用runInAction解決。
@action:修飾的函數必須注意,如果其內部調用了非**@action.bound修飾的異步回調函數,會報錯,因爲回調函數不在當前上下文環境中,導致this指向錯誤!可以使用action或者runInAction**包裹代碼來解決。
@action.bound:該裝飾器將函數上下文綁定了,可以放在其他上下文中依然正確執行,這種寫法在將方法傳給callback時非常有用
var run = dStore.run
run()
runInAction
隨時定義匿名的action然後運行,接收一個無參函數
注意:在異步或者回調函數中,@action範圍在僅僅在代碼塊,直到第一個await,如果後續還有其他的await,需要使用runInAction包裹!
observer
屬於mobx-react包,與react結合使用,在會調用函數導致改變mobx狀態的react組件上註解
Provider
屬於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
inject
屬於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;
})