初識react(四) react中非同步解決方案之 redux-saga

言sir發表於2018-09-19

初識react(四) react中非同步解決方案之 redux-saga

回顧

今天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函式

初識react(四) react中非同步解決方案之 redux-saga

最後在梳理下整個過程

  • 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、頁面重新整理

  • 更多優質文章參考

  • redux所有原始碼解析戳這裡

相關文章