回顧
- 初識react(一) 揭開jsx語法和虛擬DOM面紗
- 初識react(二) 實現一個簡版的html+redux.js的demo
- 初識react(三)在 react中使用redux來實現簡版計數器
- 初識react(四) react中非同步解決方案之 redux-saga
- 初識react(五) 資料流終極解決方案 dva(零配置)
今天demo是實現一個非同步的計算器,探究redux-saga工作流程
簡介
- redux-saga 是一個 redux 的中介軟體,而中介軟體的作用是為 redux 提供額外的功能。
- 由於在 reducers 中的所有操作都是同步的並且是純粹的,即 reducer 都是純函式,純函式是指一個函式的返回結果只依賴於它的引數,並且在執行過程中不會對外部產生副作用,即給它傳什麼,就吐出什麼。
- 但是在實際的應用開發中,我們希望做一些非同步的(如Ajax請求)且不純粹的操作(如改變外部的狀態),這些在函數語言程式設計正規化中被稱為“副作用”。
redux-saga 就是用來處理上述副作用(非同步任務)的一箇中介軟體。它是一個接收事件,並可能觸發新事件的過程管理者,為你的應用管理複雜的流程。
redux-saga工作原理
- 對generator不瞭解的,看下阮一峰 generator講解
- sages 採用 Generator 函式來 yield Effects(包含指令的文字物件)。
- Generator 函式的作用是可以暫停執行,再次執行的時候從上次暫停的地方繼續執行
- Effect 是一個簡單的物件,該物件包含了一些給 middleware 解釋執行的資訊。
- 你可以通過使用 effects API 如 fork,call,take,put,cancel 等來建立 Effect。
redux-saga分類
- worker saga 做左右的工作,如呼叫API,進行非同步請求,獲取非同步封裝結果
- watcher saga 監聽被dispatch的actions,當接受到action或者知道其被觸發時,呼叫worker執行任務
- root saga 立即啟動saga的唯一入口
基本介紹已經講完了,當做完一個demo後,回頭再看redux-saga官網或者上面講解,可能會有更深的體會
使用redux-saga實現一個非同步計數器
由於目錄結構跟上篇文章一樣,在這裡就只把變動的部分單獨抽離出來講解
1、修改actions/counter.js
- 增加一個非同步記數的動作型別。
import * as Types from "../action-types";
let actions ={
add(num){
return{type:Types.INCREMENT,count:num}
},
minus(num){
return{type:Types.DECREMENT,count:num}
},
//增加了一個非同步記數的型別,用於在counter.js中派發動作
async(num){
return {type:Types.ADD_ASYNC}
}
};
export default actions;
複製程式碼
2、重點,在src目錄下增加saga.js檔案
//takeEvery=>負責監聽 put=>派發動作 call=>告訴saga,執行delay,並傳入1000作為引數
import {takeEvery,put,call} from "redux-saga/effects";
import * as Types from "./store/action-types";
const delay = ms=>new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve()
},ms)
})
//saga分為三類 1、rootsaga 2、監聽saga 3、worker幹活的saga
function* add() {
yield call(delay,1000);
//就是指揮saga中介軟體向倉庫派發動作
yield put({type:Types.INCREMENT,count:10});
}
function* watchAdd() {
//監聽派發給倉庫的動作,如果動作型別匹配的話,會執行對應的監聽生成器
yield takeEvery(Types.ADD_ASYNC,add)
}
export default function* rootSaga() {
yield watchAdd()
}
複製程式碼
還記得上面說的,rudux-saga分類,root saga ->watcher saga -> worker saga。在這段程式碼中將會體現,程式碼我們從上往下看。
2.1 saga工作流程(程式碼從下往上看)
- 預設匯出了rootSaga,即saga的入口檔案
- watcher saga ->wactchAdd 負責監聽派發的動作,如果動作型別匹配,執行對應的 worker saga
- 上面的worker saga指代的是 add函式
2.2 redux-saga/effects 副作用
在實際的應用開發中,我們希望做一些非同步的(如Ajax請求)且不純粹的操作(如改變外部的狀態),這些在函數語言程式設計正規化中被稱為“副作用”。
- takeEvery=>負責監聽,監聽派發給倉庫的動作,如果動作型別匹配的話,會執行對應的監聽生成器->add
- put=>派發動作,可以理解為dispatch
- call=>告訴saga,執行delay函式,並把引數傳過去。注意: delay函式必須返回promise
講到這裡,流程就說完了,接下來在store中執行rootSaga
3、在store/index引入rootSaga
import {createStore,applyMiddleware} from 'redux';
import createSagaMiddleware from "redux-saga"; //引入redux-saga
import rootSaga from "../saga" //引入我們上面寫好的rootSaga
import reducer from "./reducers"
let sagaMiddleware =createSagaMiddleware(); //執行得到saga中介軟體
let store = createStore(reducer,applyMiddleware(sagaMiddleware)); //使用中介軟體
sagaMiddleware.run(rootSaga); //開始執行rootSaga
export default store;
複製程式碼
對於redux中介軟體沒有講解,這部分內容涉及東西比較多,也不太好理解,寫這個react系列目的是儘可能簡單的讓所有人理解,想看所有的redux原始碼解析,底部會留下所有總結的程式碼倉庫。
4、在counter.js元件中派發這個非同步動作
- 程式碼跟上篇文章一模一樣,只是增加了按鈕實現非同步操作
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {connect} from "react-redux";
import actions from "../store/actions/counter"
class Counter extends Component {
render() {
console.log(this.props);
return(
<div>
<h1>{this.props.number}</h1>
<button onClick={()=>{this.props.add(5)}}>+</button>
<button onClick={()=>{this.props.minus(1)}}>-</button>
//增加非同步操作
<button onClick={()=>{this.props.async()}}>非同步加10</button>
</div>
)
}
}
export default connect(state=>({
...state
}),actions)(Counter)
複製程式碼
終結,看效果。可以看出,點選後等待1s才加10。那我們就可以在call()中傳入執行的非同步函式(如ajax)來獲取資料啦。我們這個例對應的delay函式
最後在梳理下整個過程
-
1、元件中呼叫了this.props.async(),返回的action物件=>{type:Types.ADD_ASYNC}會在connect方法中被派發
-
2、saga中takeEvery(Types.ADD_ASYNC,add),監聽到動作的型別後,觸發 worker saga =>add
-
3、worker saga中先 yield call(delay,1000); 執行delay方法,延時1s
-
4、yield put({type:Types.INCREMENT,count:10}); 最後派發的還是INCREMENT的型別
-
5、接著被reducer處理,更新state
-
6、頁面重新整理