react系列筆記:第三記-redux-saga

github : https://github.com/redux-saga/redux-sagahtml

文檔:https://redux-saga.js.org/  git

 

redux-saga:  redux中間件,旨在處理應用中的反作用github

使用:redux

import createSagaMiddleware from 'redux-saga'
import {createStore,applyMiddleware} from 'redux'

const saga = createSagaMiddleware();

const store = createStore(
  reducer,
  applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(mysagas);
  

 

基礎概念:promise

  saga-middleware 檢查每一個被 yield 的 Effect 的類型,而後決定如何實現哪一個 Effect。若是 Effect 類型是 PUT 那 middleware 會 dispatch 一個 action 到 Store。 若是 Effect 類型是 CALL 那麼它會調用給定的函數。app

put({type: 'INCREMENT'}) // => { PUT: {type: 'INCREMENT'} }
call(delay, 1000)        // => { CALL: {fn: delay, args: [1000]}}

輔助函數:異步

  takeEvery : 能夠同時啓動屢次任務函數

  takeLatest : 在一次任務未完成以前,遇到新任務將取消以前任務。post

聲明式的effect測試

  call:  saga經過 Generator函數實現,在yield函數後執行effect,其中call是用於執行某些異步操做的。

yield requestSome('/xxx');

yield call(requestSome,'/xxx')
//之因此用call取代上面的直接調用寫法,好處在於,
//編寫測試代碼的時候,能夠deepEqual  generator函數的next().value和call(requestSome,'/xxx'),這樣全部的effect均可以被測試,而不須要mock yield後的執行結果

dispatching actions:

  put : 和上面的call同樣,中間件提供put 來把action丟到中間件中去dispatch,好處一樣是便於測試

 

高級:

take:

  call方法能夠yield一個promise,而後會阻塞generator的執行,知道promise resolve,結果返回
  take(xxx)同理,阻塞generator直到xxx匹配的action被觸發

  由此:take(*)能夠用於抓取log,take(*)會匹配任意的action觸發事件

  使用while(true){ take(/xxx/) }  能夠建立持續的action監聽,固然也能夠根據需求,選擇性的監聽,只需改版while(xxx)的條件就好了

function* actionLog(){
  while(true){
    yield take('INCREMENT');
    console.log('do increment');
    yield take('DECREMENT');
    console.log('do decrement')
  }
}
//take能夠控制action的監聽順序,如上,會先監聽到INCREMENT以後,纔會再往下監聽DECREMENT,完後繼續監聽INCREMENT。這在一些順序明確的action事件裏,能夠將流程代碼寫在一塊兒,
//如login   logout  

 

fork:

  fork和take不一樣,take會和call同樣阻塞代碼的執行,知道結果返回,fork則不會,它會將任務啓動而且不阻塞代碼的執行,

  fork會返回一個task,能夠用cacel(task)來取消任務

  https://redux-saga.js.org/docs/advanced/NonBlockingCalls.html

  此文中將login 和logout做爲例子,在login請求未返回來以前執行了logout,則須要cacel未完成的login,又不能用take(login)不然阻塞logout,會在login響應以前take不到logout。

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

// ...

function* loginFlow() {
  while (true) {
    const {user, password} = yield take('LOGIN_REQUEST')
    // fork return a Task object
    const task = yield fork(authorize, user, password)
    const action = yield take(['LOGOUT', 'LOGIN_ERROR'])
    if (action.type === 'LOGOUT')
      yield cancel(task)
    yield call(Api.clearItem, 'token')
  }
}


import { take, call, put, cancelled } 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})
    yield call(Api.storeItem, {token})
    return token
  } catch(error) {
    yield put({type: 'LOGIN_ERROR', error})
  } finally {
    if (yield cancelled()) {
      // ... put special cancellation handling code here
    }
  }
}

all:

  yield表達式,能夠將語句分段執行,但若是有時候想同時執行兩個任務,則須要用到all

import {all,call} from 'redux-saga/effect'

//此處會同步執行兩個call的任務
const [users, repos] = yield all([
  call(fetch, '/users'),
  call(fetch, '/repos')
])

race:

  和promise中的race一個概念,執行多個任務,受到響應後則繼續執行  

function* fetchPostsWithTimeout() {
  const {posts, timeout} = yield race({
    posts: call(fetchApi, '/posts'),
    timeout: call(delay, 1000)
  })

  if (posts)
    put({type: 'POSTS_RECEIVED', posts})
  else
    put({type: 'TIMEOUT_ERROR'})
}

yield * 

  經過yield * xxx()來組合多個generator任務。

組合saga:

  能夠經過yield [call(task1),call(task2)...]來組合多個generator。

 

channels:

 

throttle:

  節流

 

delay:

  防抖動

相關文章
相關標籤/搜索