詳解redux異步操作實(shí)踐
一、redux基礎(chǔ)
redux
- 通過 dispatch(action) -> 中間件 -> reducer處理數(shù)據(jù) -> 改變store -> 使用subscribe()監(jiān)聽store改變更新視圖 的方式管理狀態(tài)
- 將所有狀態(tài)存儲(chǔ)在一個(gè)store對(duì)象里面
- reducer為純函數(shù),而異步操作由于結(jié)果的不確定性所以含有副作用,所以需要特殊處理
react-redux
- 容器組件,負(fù)責(zé)管理數(shù)據(jù)和業(yè)務(wù)邏輯,不負(fù)責(zé)UI呈現(xiàn)
- UI組件,提供UI呈現(xiàn),無(wú)狀態(tài)即不使用this.state,狀態(tài)全部由this.props提供
- 由connect生成容器組件,每次store改變會(huì)調(diào)用connect,connect接收兩個(gè)參數(shù): mapStateToProps, mapDispatchToProps
- mapStateToProps,將狀態(tài)映射到UI組件的props
- mapDispatchToProps,將dispatch方法映射到UI組件的props
- Provider組件,使用content API將store從頂層開始傳到每一層component供connect使用
二、redux處理異步的中間件
redux-thunk
- redux-thunk中間件允許action是一個(gè)方法
- 中間件收到action后會(huì)執(zhí)行action方法并將結(jié)果提供給reducer
- action混亂導(dǎo)致不易維護(hù)
redux-saga
- saga會(huì)監(jiān)聽action并基于這個(gè)action執(zhí)行Effects操作
- Effects提供靈活的API,包括阻塞、非阻塞調(diào)用,取消、等待、race等操作
- 方便隔離并執(zhí)行異步操作,并易于測(cè)試
三、redux-request-async-middleware
先從redux文檔中的異步action說起,每個(gè)接口調(diào)用需要dispatch三個(gè)同步action,分別是:
- 一種通知 reducer 請(qǐng)求開始的 action。對(duì)于這種 action,reducer 可能會(huì)切換一下 state 中的 isFetching 標(biāo)記。以此來(lái)告訴 UI 來(lái)顯示加載界面。
- 一種通知 reducer 請(qǐng)求成功的 action。對(duì)于這種 action,reducer 可能會(huì)把接收到的新數(shù)據(jù)合并到 state 中,并重置 isFetching。UI 則會(huì)隱藏加載界面,并顯示接收到的數(shù)據(jù)。
- 一種通知 reducer 請(qǐng)求失敗的 action。對(duì)于這種 action,reducer 可能會(huì)重置 isFetching。另外,有些 reducer 會(huì)保存這些失敗信息,并在 UI 里顯示出來(lái)。
也就是一個(gè)接口發(fā)起是這樣的
dispatch(fetchPostsRequest(subject)); fetch(url).then(res => { dispatch(fetchPostsSuccess(subject, res)); }).catch(e => { dispatch(fetchPostsFailure(subject, e)); })
而我做的事情只是將這個(gè)操作封裝進(jìn)中間件里,特殊的地方在于:
- 所有的異步請(qǐng)求共用這三個(gè)action
- 用subject來(lái)區(qū)分是哪一個(gè)請(qǐng)求
- 將所有的結(jié)果都放到store.requests里
中間件源碼
export const reduxRequest = store => next => action => { let result = next(action); let { type, subject, model } = action; let _next = action.next; if(type === FETCH_POSTS_REQUEST) { model().then(response => { _next && _next(response); store.dispatch(fetchPostsSuccess(subject, response)); }).catch(error => { console.error(error); store.dispatch(fetchPostsFailure(subject, error)); }); } return result };
- 和redux-thunk一樣,將方法放進(jìn)action里
- 中間件攔截FETCH_POSTS_REQUEST action,并進(jìn)行異步處理
reducer源碼
export const requests = (state = {}, action) => { switch (action.type) { case FETCH_POSTS_REQUEST: return assign({}, state, { [action.subject]: { isFetching: true, state: 'loading', subject: action.subject, response: null, error: null, } } ); case FETCH_POSTS_FAILURE: return assign({}, state, { [action.subject]: { isFetching: false, state: 'error', subject: action.subject, response: state[action.subject].response, error: action.error, } } ); case FETCH_POSTS_SUCCESS: return assign({}, state, { [action.subject]: { isFetching: false, state: 'success', subject: action.subject, response: action.response, } } ); case FETCH_POSTS_CLEAR: return assign({}, state, { [action.subject]: { isFetching: false, state: 'cleared', subject: null, response: null, error: null, } } ); default: return state; } }
- 將結(jié)果放入該subject對(duì)應(yīng)下的response,如果錯(cuò)誤的話將錯(cuò)誤信息放入error當(dāng)中
- isFetching表示當(dāng)前的請(qǐng)求狀態(tài)
- 另外還加入了當(dāng)前的狀態(tài)state和subject信息
將請(qǐng)求進(jìn)行封裝
const request = (subject, model, next) => { _dispatch(fetchPostsRequest(subject, model, next)); return true; };
- 寫一個(gè)方法來(lái)發(fā)起FETCH_POSTS_REQUEST action
- 也就是說寫請(qǐng)求的時(shí)候不用再管action這東西了,直接調(diào)用request方法
將結(jié)果進(jìn)行封裝
const getResponse = state => state && state.response !== null && state.response; const getLoading = (states = []) => states.reduce((pre, cur) => pre || (cur && cur.isFetching) , false) || false;
- 可以獲取結(jié)果和多個(gè)請(qǐng)求下loading的狀態(tài)
- 有更多的操作或者格式還可以繼續(xù)封裝,比如列表
使用方法redux-request-async-middleware
四、總結(jié)
- 使用了redux來(lái)進(jìn)行狀態(tài)管理,而并不需要編寫redux那一套復(fù)雜邏輯,最大程度的減少異步操作的復(fù)雜度
- 適用于前端通過接口來(lái)處理和存儲(chǔ)數(shù)據(jù)的項(xiàng)目
- 接口由redux處理,而視圖組件由內(nèi)部state來(lái)處理,而外部只暴露簡(jiǎn)單的接口來(lái)進(jìn)行操作,分離業(yè)務(wù)層和視圖層
- 對(duì)比react 16.3 new content API,redux的優(yōu)勢(shì)在于熱插播的中間件和純函數(shù)reducer寫法
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
微信小程序自定義可滑動(dòng)頂部TabBar選項(xiàng)卡實(shí)現(xiàn)頁(yè)面切換功能示例
這篇文章主要介紹了微信小程序自定義可滑動(dòng)頂部TabBar選項(xiàng)卡實(shí)現(xiàn)頁(yè)面切換功能,結(jié)合實(shí)例形式分析了微信小程序自定義頂部TabBar選項(xiàng)卡頁(yè)面切換功能的相關(guān)布局、樣式及功能實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-05-05基于JavaScript判斷瀏覽器到底是關(guān)閉還是刷新(超準(zhǔn)確)
這篇文章主要介紹了基于JavaScript判斷瀏覽器到底是關(guān)閉還是刷新(超準(zhǔn)確)的相關(guān)資料,需要的朋友可以參考下2016-02-02Javascript 代碼也可以變得優(yōu)美的實(shí)現(xiàn)方法
Javascript 代碼也可以變得優(yōu)美的一些經(jīng)驗(yàn)小結(jié)。2009-06-06跟我學(xué)Nodejs(三)--- Node.js模塊
這是本系列的第三篇文章了,前面2篇網(wǎng)友們反饋回來(lái)不少的消息,加上最近2天比較忙,一直沒來(lái)得及整理,周末了,趕緊給大家整理下發(fā)出來(lái),本文講的是node.js模塊2014-05-05淺談js基礎(chǔ)數(shù)據(jù)類型和引用類型,深淺拷貝問題,以及內(nèi)存分配問題
下面小編就為大家?guī)?lái)一篇淺談js基礎(chǔ)數(shù)據(jù)類型和引用類型,深淺拷貝問題,以及內(nèi)存分配問題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧2017-09-09