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

回顧

今天demo是實現一個異步的計算器,探究redux-saga工做流程html

簡介

  • redux-saga 是一個 redux 的中間件,而中間件的做用是爲 redux 提供額外的功能。
  • 因爲在 reducers 中的全部操做都是同步的而且是純粹的,即 reducer 都是純函數,純函數是指一個函數的返回結果只依賴於它的參數,而且在執行過程當中不會對外部產生反作用,即給它傳什麼,就吐出什麼。
  • 可是在實際的應用開發中,咱們但願作一些異步的(如Ajax請求)且不純粹的操做(如改變外部的狀態),這些在函數式編程範式中被稱爲「反作用」。

redux-saga 就是用來處理上述反作用(異步任務)的一箇中間件。它是一個接收事件,並可能觸發新事件的過程管理者,爲你的應用管理複雜的流程。react

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官網或者上面講解,可能會有更深的體會git

使用redux-saga實現一個異步計數器

因爲目錄結構跟上篇文章同樣,在這裏就只把變更的部分單獨抽離出來說解github

一、修改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;

複製代碼

二、重點,在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分爲三類 一、rootsaga 二、監聽saga 三、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。在這段代碼中將會體現,代碼咱們從上往下看。ajax

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中執行rootSagaredux

三、在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源碼解析,底部會留下全部總結的代碼倉庫。promise

四、在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函數bash

最後在梳理下整個過程

  • 一、組件中調用了this.props.async(),返回的action對象=>{type:Types.ADD_ASYNC}會在connect方法中被派發app

  • 二、saga中takeEvery(Types.ADD_ASYNC,add),監聽到動做的類型後,觸發 worker saga =>add

  • 三、worker saga中先 yield call(delay,1000); 執行delay方法,延時1s

  • 四、yield put({type:Types.INCREMENT,count:10}); 最後派發的仍是INCREMENT的類型

  • 五、接着被reducer處理,更新state

  • 六、頁面刷新

  • 更多優質文章參考

  • redux全部源碼解析戳這裏

相關文章
相關標籤/搜索