摘要:通過創建將所有的異步操作邏輯收集在一個地方集中處理,可以用來代替中間件。
redux-saga框架使用詳解及Demo教程
前面我們講解過redux框架和dva框架的基本使用,因為dva框架中effects模塊設計到了redux-saga中的知識點,可能有的同學們會用dva框架,但是對redux-saga又不是很熟悉,今天我們就來簡單的講解下saga框架的主要API和如何配合redux框架使用
redux-saga 官方地址http://leonshi.com/redux-saga-in-chinese/index.html
Demo運行效果圖todoList
CounterApp
示例Demo地址redux-saga-Demo作者還是按照以前的風格,提供了兩個不同的版本,簡單的 CounterApp, 稍復雜的 TodoList
CounterApp
https://github.com/guangqiang-liu/redux-saga-counterApp
todoList
https://github.com/guangqiang-liu/redux-saga-todoListDemo
什么是redux-sagaredux-saga 是一個用于管理 Redux 應用異步操作的中間件(又稱異步 action)。 redux-saga 通過創建 Sagas 將所有的異步操作邏輯收集在一個地方集中處理,可以用來代替 redux-thunk 中間件。
這意味著應用的邏輯會存在兩個地方:
Reducers 負責處理 action 的 state 更新
Sagas 負責協調那些復雜或異步的操作
Sagas是通過Generator函數來創建的,如果有不熟悉 Generator函數使用的,請查看阮老師對Generator的介紹
Sagas 不同于thunks,thunks 是在action被創建時調用,而 Sagas只會在應用啟動時調用(但初始啟動的 Sagas 可能會動態調用其他 Sagas),Sagas 可以被看作是在后臺運行的進程,Sagas 監聽發起的action,然后決定基于這個 action來做什么:是發起一個異步調用(比如一個 fetch 請求),還是發起其他的action到Store,甚至是調用其他的 Sagas
在 redux-saga 的世界里,所有的任務都通用 yield Effects 來完成(Effect 可以看作是 redux-saga 的任務單元)。Effects 都是簡單的 Javascript 對象,包含了要被 Saga middleware 執行的信息(打個比方,你可以看到 Redux action其實是一個個包含執行信息的對象), redux-saga 為各項任務提供了各種Effect創建器,比如調用一個異步函數,發起一個action到Store,啟動一個后臺任務或者等待一個滿足某些條件的未來的 action
redux-saga框架核心API一、Saga 輔助函數
redux-saga提供了一些輔助函數,用來在一些特定的action 被發起到Store時派生任務,下面我先來講解兩個輔助函數:takeEvery 和 takeLatest
takeEvery
例如:每次點擊 Fetch 按鈕時,我們發起一個 FETCH_REQUESTED 的 action。 我們想通過啟動一個任務從服務器獲取一些數據,來處理這個action
首先我們創建一個將執行異步 action 的任務:
import { call, put } from "redux-saga/effects" export function* fetchData(action) { try { const data = yield call(Api.fetchUser, action.payload.url); yield put({type: "FETCH_SUCCEEDED", data}); } catch (error) { yield put({type: "FETCH_FAILED", error}); } }
然后在每次 FETCH_REQUESTED action 被發起時啟動上面的任務
import { takeEvery } from "redux-saga" function* watchFetchData() { yield* takeEvery("FETCH_REQUESTED", fetchData) }
注意:上面的 takeEvery 函數可以使用下面的寫法替換
function* watchFetchData() { while(true){ yield take("FETCH_REQUESTED"); yield fork(fetchData); } }
takeLatest
在上面的例子中,takeEvery 允許多個 fetchData 實例同時啟動,在某個特定時刻,我們可以啟動一個新的 fetchData 任務, 盡管之前還有一個或多個 fetchData 尚未結束
如果我們只想得到最新那個請求的響應(例如,始終顯示最新版本的數據),我們可以使用 takeLatest 輔助函數
import { takeLatest } from "redux-saga" function* watchFetchData() { yield* takeLatest("FETCH_REQUESTED", fetchData) }
和takeEvery不同,在任何時刻 takeLatest 只允許執行一個 fetchData 任務,并且這個任務是最后被啟動的那個,如果之前已經有一個任務在執行,那之前的這個任務會自動被取消
二、Effect Creators
redux-saga框架提供了很多創建effect的函數,下面我們就來簡單的介紹下開發中最常用的幾種
take(pattern)
put(action)
call(fn, ...args)
fork(fn, ...args)
select(selector, ...args)
take(pattern)
take函數可以理解為監聽未來的action,它創建了一個命令對象,告訴middleware等待一個特定的action, Generator會暫停,直到一個與pattern匹配的action被發起,才會繼續執行下面的語句,也就是說,take是一個阻塞的 effect
用法:
function* watchFetchData() { while(true) { // 監聽一個type為 "FETCH_REQUESTED" 的action的執行,直到等到這個Action被觸發,才會接著執行下面的 yield fork(fetchData) 語句 yield take("FETCH_REQUESTED"); yield fork(fetchData); } }
put(action)
put函數是用來發送action的 effect,你可以簡單的把它理解成為redux框架中的dispatch函數,當put一個action后,reducer中就會計算新的state并返回,注意: put 也是阻塞 effect
用法:
export function* toggleItemFlow() { let list = [] // 發送一個type為 "UPDATE_DATA" 的Action,用來更新數據,參數為 `data:list` yield put({ type: actionTypes.UPDATE_DATA, data: list }) }
call(fn, ...args)
call函數你可以把它簡單的理解為就是可以調用其他函數的函數,它命令 middleware 來調用fn 函數, args為函數的參數,注意: fn 函數可以是一個 Generator 函數,也可以是一個返回 Promise 的普通函數,call 函數也是阻塞 effect
用法:
export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) export function* removeItem() { try { // 這里call 函數就調用了 delay 函數,delay 函數為一個返回promise 的函數 return yield call(delay, 500) } catch (err) { yield put({type: actionTypes.ERROR}) } }
fork(fn, ...args)
fork 函數和 call 函數很像,都是用來調用其他函數的,但是fork函數是非阻塞函數,也就是說,程序執行完 yield fork(fn, args) 這一行代碼后,會立即接著執行下一行代碼語句,而不會等待fn函數返回結果后,在執行下面的語句
用法:
import { fork } from "redux-saga/effects" export default function* rootSaga() { // 下面的四個 Generator 函數會一次執行,不會阻塞執行 yield fork(addItemFlow) yield fork(removeItemFlow) yield fork(toggleItemFlow) yield fork(modifyItem) }
select(selector, ...args)
select 函數是用來指示 middleware調用提供的選擇器獲取Store上的state數據,你也可以簡單的把它理解為redux框架中獲取store上的 state數據一樣的功能 :store.getState()
用法:
export function* toggleItemFlow() { // 通過 select effect 來獲取 全局 state上的 `getTodoList` 中的 list let tempList = yield select(state => state.getTodoList.list) }
三、createSagaMiddleware()
createSagaMiddleware 函數是用來創建一個 Redux 中間件,將 Sagas 與 Redux Store 鏈接起來
sagas 中的每個函數都必須返回一個 Generator 對象,middleware 會迭代這個 Generator 并執行所有 yield 后的 Effect(Effect 可以看作是 redux-saga 的任務單元)
用法:
import {createStore, applyMiddleware} from "redux" import createSagaMiddleware from "redux-saga" import reducers from "./reducers" import rootSaga from "./rootSaga" // 創建一個saga中間件 const sagaMiddleware = createSagaMiddleware() // 創建store const store = createStore( reducers, 將sagaMiddleware 中間件傳入到 applyMiddleware 函數中 applyMiddleware(sagaMiddleware) ) // 動態執行saga,注意:run函數只能在store創建好之后調用 sagaMiddleware.run(rootSaga) export default store
四、middleware.run(sagas, ...args)
動態執行sagas,用于applyMiddleware階段之后執行sagas
sagas: Function: 一個 Generator 函數
args: Array: 提供給 saga 的參數 (除了 Store 的 getState 方法)
注意:動態執行saga語句 middleware.run(sagas) 必須要在store創建好之后才能執行,在 store 之前執行,程序會報錯
以CounterApp Demo來看redux-saga具體使用方式index.js
import React from "react"; import ReactDOM from "react-dom"; import {createStore, applyMiddleware} from "redux" import createSagaMiddleware from "redux-saga" import rootSaga from "./sagas" import Counter from "./Counter" import rootReducer from "./reducers" const sagaMiddleware = createSagaMiddleware() let middlewares = [] middlewares.push(sagaMiddleware) const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore) const store = createStoreWithMiddleware(rootReducer) sagaMiddleware.run(rootSaga) const action = type => store.dispatch({ type }) function render() { ReactDOM.render(action("INCREMENT")} onDecrement={() => action("DECREMENT")} onIncrementAsync={() => action("INCREMENT_ASYNC")} />, document.getElementById("root") ) } render() store.subscribe(render)
sagas.js
import { put, call, take,fork } from "redux-saga/effects"; import { takeEvery, takeLatest } from "redux-saga" export const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); function* incrementAsync() { // 延遲 1s 在執行 + 1操作 yield call(delay, 1000); yield put({ type: "INCREMENT" }); } export default function* rootSaga() { // while(true){ // yield take("INCREMENT_ASYNC"); // yield fork(incrementAsync); // } // 下面的寫法與上面的寫法上等效 yield* takeEvery("INCREMENT_ASYNC", incrementAsync) }
reducer.js
export default function counter(state = 0, action) { switch (action.type) { case "INCREMENT": return state + 1 case "DECREMENT": return state - 1 case "INCREMENT_ASYNC": return state default: return state } }
從上面的代碼結構可以看出,redux-saga的使用方式還是比較簡單的,相比較之前的redux框架的CounterApp,多了一個sagas的文件,reducers文件還是之前的使用方式
總結本文所講解的基本上是redux-saga框架在開發中最常使用到的常用API,還有很多不常用API,請參照redux-saga官方文檔:http://leonshi.com/redux-saga-in-chinese/docs/api/index.html
如果同學們看到文章這里,還是對redux-saga框架的基本使用不熟悉,概念模糊,建議看看作者提供的Demo示例
作者建議:同學們可以將作者之前講解的redux框架和redux-saga框架對比來學習理解,這樣更清楚他們之間的區別和聯系。
更多文章作者React Native開源項目OneM【500+ star】地址(按照企業開發標準搭建框架完成開發的):https://github.com/guangqiang-liu/OneM:歡迎小伙伴們 star
作者簡書主頁:包含60多篇RN開發相關的技術文章http://www.jianshu.com/u/023338566ca5 歡迎小伙伴們:多多關注,多多點贊
作者React Native QQ技術交流群:620792950 歡迎小伙伴進群交流學習
友情提示:在開發中有遇到RN相關的技術問題,歡迎小伙伴加入交流群(620792950),在群里提問、互相交流學習。交流群也定期更新最新的RN學習資料給大家,謝謝大家支持!
小伙伴們掃下方二維碼加入RN技術交流QQ群文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97516.html
摘要:框架的使用詳解及教程在前段時間,我們也學習講解過框架的基本使用,但是有很多同學在交流群里給我的反饋信息說,框架理解上有難度,看了之后還是一臉懵逼不知道如何下手,很多同學就轉向選擇使用框架。 dva框架的使用詳解及Demo教程 在前段時間,我們也學習講解過Redux框架的基本使用,但是有很多同學在交流群里給我的反饋信息說,redux框架理解上有難度,看了之后還是一臉懵逼不知道如何下手,很...
摘要:特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 特意對前端學習資源做一個匯總,方便自己學習查閱參考,和好友們共同進步。 本以為自己收藏的站點多,可以很快搞定,沒想到一入匯總深似海。還有很多不足&遺漏的地方,歡迎補充。有錯誤的地方,還請斧正... 托管: welcome to git,歡迎交流,感謝star 有好友反應和斧正,會及時更新,平時業務工作時也會不定期更...
摘要:前端日報精選漫談函數式編程一十年蹤跡的博客前端每周清單的優勢與劣勢有望超越在嵌入式及物聯網的應用現狀進階系列高階組件詳解一前端之路譯如何充分利用控制臺掘金程序猿升級攻略眾成翻譯中文譯如何充分利用控制臺掘金前端從強制開啟壓縮探 2017-06-27 前端日報 精選 漫談 JS 函數式編程(一) - 十年蹤跡的博客前端每周清單: Vue的優勢與劣勢;Node.js有望超越Java;JS在嵌...
摘要:在的中,可以使用或者等來監聽某個,當某個觸發后,可以使用等發起異步操作,操作完成后使用函數觸發,同步更新,從而完成整個的更新。對于何時響應和如何響應,并沒有控制權。的作用是用來取消一個還未返回的任務。 項目地址 項目截圖 showImg(https://segmentfault.com/img/bVUJwx?w=718&h=1294); redux-saga介紹 眾所周知,react僅...
閱讀 2914·2021-11-15 18:02
閱讀 3806·2021-10-14 09:43
閱讀 3745·2021-09-08 10:41
閱讀 2526·2019-08-30 15:53
閱讀 1808·2019-08-30 14:14
閱讀 1950·2019-08-29 16:12
閱讀 3147·2019-08-29 14:03
閱讀 1283·2019-08-29 13:46