天天看點

redux-saga 是什麼?

前言

在使用react redux的時候,會經常遇到需要處理異步action的情況。處理異步action的方法有幾種。其中redux-thunk,redux-saga都是處理異步action的中間件。利用這些中間件可以很好的達到我們預期效果

redux-saga

redux-saga

是一個用于管理應用程式 Side Effect(副作用,例如異步擷取資料,通路浏覽器緩存等)的 library,它的目标是讓副作用管理更容易,執行更高效,測試更簡單,在處理故障時更容易。

個人是這麼了解redux-saga的,在app中注入redux-saga中間件後,saga effects函數中對相對應的action進行監聽,在代碼中執行觸發dispatch 對應action的時候,saga的effects 函數會對其進行攔截處理,中途進行一些異步、同步、或者是執行調用其他effects函數。在effects函數中,可以同步的方式書寫異步代碼。相對于redux-thunk,saga的優勢在于effects函數中對各種異步的處理比較友善和容易拓展。

計數器demo例子

Counter 元件

import React, { Component, PropTypes } from 'react'

const Counter = ({ value, onIncrement, onDecrement, onIncrementAsync }) =>
      <div>
        <button onClick={onIncrement}>
          Increment
        </button>
        {' '}
        <button onClick={onDecrement}>
          Decrement
        </button>
        <button onClick={onIncrementAsync}>
          delay Increment
        </button>
        <hr />
        <div>
          Clicked: {value} times
        </div>
      </div>

Counter.propTypes = {
  value: PropTypes.number.isRequired,
  onIncrement: PropTypes.func.isRequired,
  onDecrement: PropTypes.func.isRequired,
  onIncrementAsync:  PropTypes.func.isRequired
}
export default Counter      

main.js

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import rootSaga from './saga'
import Counter from './Counter'
import reducer from './reducers'

const sagaMiddleware = createSagaMiddleware()
const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)
const action = type => store.dispatch({type})

function render() {
  ReactDOM.render(
    <Counter
      value={store.getState()}
      onIncrement={() => action('INCREMENT')}
      onDecrement={() => action('DECREMENT')}
      onIncrementAsync={() => action('INCREMENT_ASYNC')} />,
    document.getElementById('root')
  )
}

render()
store.subscribe(render)      

saga.js

import { delay } from 'redux-saga'
import { put, takeEvery, all } from 'redux-saga/effects'

export function* incrementAsync() {
    yield delay(1000)
    yield put({ type: 'INCREMENT' })
}
export function* watchIncrementAsync() {
    yield takeEvery('INCREMENT_ASYNC', incrementAsync)
}
export default function* rootSaga() {
    yield all([
        watchIncrementAsync()
    ])
}      

在計數器中有三個按鈕 

Increment

Decrement

Delay Increment

點選

Increment

,計數馬上加1,點選

Decrement

,計數馬上減1,點選

Delay Increment

計數延遲1秒加1

原了解析在saga中,對于

INCREMENT_ASYNC

這個action進行了監聽,如果觸發了這個action則會執行saga中的incrementAsync函數,在該函數中,有delay延遲函數。yield delay(1000)的意思是等待延遲1秒,然後執行yield put(…),執行觸發

INCREMENT

action,然後觸發reducer 對state的值的修改。

整個流程 監聽

INCREMENT_ASYNC

,觸發

INCREMENT_ASYNC

,執行incrementAsync,dispatch

INCREMENT

,最後修改state,觸發UI更新。在saga的effects函數中可以對異步處理的結果進行再次處理整合,最後再派發執行下一步。整個流程是有序執行可監控的。這樣我們就簡單了解了redux-saga的原理和流程,進一步學習redux-saga則需要對API有更深入的了解

進階學習redux-saga

  • take
  • fork
  • takeEvery
  • takeLatest

takeLatest & takeEvery 是監聽action,一遍一遍執行,不會停止。takeLatest與takeEvery的差別在于,如果同個action多次觸發,takeLatest隻會執行最後一個action,往前的action會取消調用,而takeEvery則每個action均會執行調用。take則隻執行一次。fork則在背景建立任務監聽,非阻塞主流程。是以會經常使用 fork+ while(true)+take的方式實作可控制的effect函數。檢視例子

import { fork, call, take, put } from 'redux-saga/effects'
import Api from '...'

function* authorize(user, password) {
  try {
    const token = yield call(Api.authorize, user, password)
    yield put({type: 'LOGIN_SUCCESS', token})
  } catch(error) {
    yield put({type: 'LOGIN_ERROR', error})
  }
}

function* loginFlow() {
  while(true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    yield fork(authorize, user, password)
    yield take(['LOGOUT', 'LOGIN_ERROR'])
    yield call(Api.clearItem('token'))
  }
}      

參考資料

  • redux-saga中文教程