Reflux怎么玩?
Reflux的action應(yīng)該全權(quán)負(fù)責(zé)async到服務(wù)器的數(shù)據(jù)處理,尤其是數(shù)據(jù)的卸載。
Reflux的store應(yīng)該分為最少3種:
- 最基本的:數(shù)據(jù)型
- 少量:broadcast廣播型
- 通道:tunnel型
一、數(shù)據(jù)型是基本數(shù)據(jù)的:
store用來存數(shù)據(jù),這些數(shù)據(jù)在邏輯上與用戶有關(guān),比如用戶的基本信息,存數(shù)據(jù)的store在以引用mixins的方式被引入react組件的時(shí)候需使用Reflux.connect方法,這樣,react組件初始化時(shí)會(huì)調(diào)用store的getInitialState和react組件的getInitialState方法合并起來提供數(shù)據(jù)到react組件的state里。注意,這個(gè)地方組件調(diào)用store的getInitialState,但是store可能早已存在于內(nèi)存里了,這個(gè)時(shí)候store里如果已有數(shù)據(jù)(比如用戶信息已經(jīng)從服務(wù)器獲取到了),就應(yīng)該把這些數(shù)據(jù)直接從store的getInitialState里返回回去,而不是再?gòu)姆?wù)器獲取一次。所以,一個(gè)方便的react組件對(duì)接數(shù)據(jù)型store時(shí)的代碼是這樣的:
store:
/* 這樣的store的基本行為模式是: */
/* 1. 初始化(init)里生成默認(rèn)數(shù)據(jù) */
/* 2. 某react的組件獲取數(shù)據(jù)的時(shí)候走到getInitialState,去后臺(tái)獲取數(shù)據(jù)(僅一次) */
/* 3. 之后由于用this.data.pristine置位過,數(shù)據(jù)會(huì)直接從getInitialState流到react組件,而不會(huì)再去服務(wù)器獲取 */
/* P.S. 謹(jǐn)慎使用或者干脆禁用開發(fā)者從component上自己調(diào)用服務(wù)器獲取數(shù)據(jù) */
const SomeDataStore = Reflux.createStore({
/* 這個(gè)只是給react組件用的,調(diào)一次執(zhí)行一次,對(duì)store而言可不是一輩子就一次 */
getInitialState: function() {
if(this.pristine) { // 放這里調(diào)action,還用一個(gè)變量控制住,只發(fā)一次后臺(tái),而且是按需發(fā)后臺(tái)
SomeDataAction.getSomeData() // 這里其實(shí)會(huì)有async的bug,少數(shù)情況下會(huì)有多個(gè)action發(fā)出,但業(yè)務(wù)邏輯明確組件結(jié)構(gòu)明確的時(shí)候確實(shí)不會(huì)bug
}
// getInitialState是react組件初始化時(shí)必須調(diào)用的方法,這里給組件提供數(shù)據(jù)
// 但是這個(gè)方法并不是store初始化自己的方法(這個(gè)坑好痛)
return this.data
},
/* init才是store的生命周期好嘛 */
init: function() { // 這才是Store初始化的方法,這里一次性生成初始化數(shù)據(jù)
this.pristine = true // 增加這個(gè)字段來表達(dá)當(dāng)前store的數(shù)據(jù)是否已從服務(wù)器獲取過
this.data = {
userName: ‘unknown’,
userId: -1,
},
},
// 數(shù)據(jù)過來后,記得 this.pristine = false
})
react component:
/* 別忘了,掛載store的組件是smart component (container) */
const SomeComponent = React.reactClass({
mixins: [ Reflux.connect(SomeDataStore, ’someData') ], // 這個(gè)mixins會(huì)把SomeDataStore返回的數(shù)據(jù)掛到this.state.someData處
getInitialState: function() {
// 你還是可以用getInitialState來初始化一些界面內(nèi)部的數(shù)據(jù)的,只是不要和store里的getInitialState沖突就好
return { uiData: ‘德瑪西亞!' }
},
})
二、少量broadcast廣播型store:
比如,好多http錯(cuò)誤,很煩,真不想讓他們跟數(shù)據(jù)混在一起,于是有這個(gè)想法:所有http錯(cuò)誤讓一個(gè)store去捕捉,捕捉后trigger出去的時(shí)候掛一個(gè)type字段,哪個(gè)組件想報(bào)錯(cuò),就去監(jiān)聽(是的,這是async,發(fā)起的component要先Reflux.listenTo到)這個(gè)store的數(shù)據(jù),用type過濾后報(bào)錯(cuò)給用戶。(所以如果你明知道自己觸發(fā)了http請(qǐng)求,但是并沒打算報(bào)錯(cuò)到用戶那里,完全可以不監(jiān)聽這個(gè)store)
const SOME_TYPE = 'some_type'
// maybe NotificationStore?
var NotifyStore = Reflux.createStore({
init: function() {
this.listenTo(Action.fetch.success, this.triggerWrapper(SOME_TYPE, true))
this.listenTo(Action.fetch.error, this.triggerWrapper(SOME_TYPE, false))
},
triggerWrapper: function(source, success) {
return function(resp) {
this.trigger({
source: source,
success: success && resp.Success,
code: resp.ErrorCode,
msg: resp.ErrorMsg, // TODO: 這里要“或”一個(gè)非200的http response
})
}
},
})
三、通道型的store:
這個(gè)store類型我其實(shí)還沒有使用過,但是在看同事寫某個(gè)翻頁(yè)列表的時(shí)候看到的:有這么一些數(shù)據(jù)并不需要存放在數(shù)據(jù)型的store里,它們只對(duì)一個(gè)界面有意義,獲取一次就拋棄了,沒必要存著。怎么辦呢?Reflux和Redux的最大區(qū)別也許也在這里:在Redux里,我們想把所有state存在store里,甚至組件只用一個(gè)render方法就寫出來了,可是Reflux里基本只存了邏輯上的數(shù)據(jù),組件的“小狀態(tài)”基本都是封裝在組件內(nèi)部的,而這部分“只對(duì)一個(gè)界面有意義”的數(shù)據(jù)正是這種“小狀態(tài)”。所以這個(gè)時(shí)候,畢竟所有http請(qǐng)求都習(xí)慣性封裝進(jìn)了action,所以做一下tunnel類的store,數(shù)據(jù)從action的async過來,經(jīng)store轉(zhuǎn)手交給react組件,不保存(沒必要保存),所以稱之為tunnel(隧道/通道).