Redux Saga簡介

Redux Saga是用於在Redux中處理副作用的庫。當您執行某項操作時,應用程序的狀態會發生一些變化,您可能需要做一些從該狀態變化中得出的事情

何時使用Redux Saga

在應用程序中使用Redux,當您執行操作時,應用程序的狀態會發生變化。

發生這種情況時,您可能需要做一些源於此狀態更改的操作。

例如,您可能想要:

  • 對服務器進行HTTP調用
  • 發送一個WebSocket事件
  • GraphQL服務器
  • 將內容保存到緩存或瀏覽器本地存儲中

…你有主意。

這些都是與應用程序狀態完全無關或異步的事物,您需要將它們移到與操作或化簡器不同的地方(從技術上講,可以,這不是擁有乾淨代碼庫的好方法)。

輸入Redux Saga,這是Redux中間件,可幫助您解決副作用。

使用Redux Saga的基本示例

為了避免在展示一些實際代碼之前投入太多理論,我簡要介紹一下如何解決構建示例應用程序時遇到的問題。

在聊天室中,當用戶編寫消息時,我會立即在屏幕上顯示該消息,以提供即時反饋。這是通過一個Redux動作

const addMessage = (message, author) => ({
  type: 'ADD_MESSAGE',
  message,
  author
})

並通過reducer更改狀態:

const messages = (state = [], action) => {
  switch (action.type) {
    case 'ADD_MESSAGE':
      return state.concat([{
        message: action.message,
        author: action.author
      }])
    default:
      return state
  }
}

您可以先導入Redux Saga,然後再應用一個來初始化Redux Saga佐賀作為Redux Store的中間件:

//...
import createSagaMiddleware from 'redux-saga'
//...

然後,我們創建一個中間件,並將其應用於我們新創建的Redux Store:

const sagaMiddleware = createSagaMiddleware()

const store = createStore( reducers, applyMiddleware(sagaMiddleware) )

最後一步是運行傳奇。我們導入它,並將其傳遞給中間件的run方法:

import handleNewMessage from './sagas'
//...
sagaMiddleware.run(handleNewMessage)

我們只需要在其中寫傳奇./sagas/index.js

import { takeEvery } from 'redux-saga/effects'

const handleNewMessage = function* handleNewMessage(params) { const socket = new WebSocket(‘ws://localhost:8989’) yield takeEvery(‘ADD_MESSAGE’, (action) => { socket.send(JSON.stringify(action)) }) }

export default handleNewMessage

該代碼的含義是:每次ADD_MESSAGE動作觸發,我們向Web套接字服務器,在這種情況下會響應localhost:8989

注意使用function*,這不是正常功能,而是發電機

它在後台如何工作

成為一個Redux中間件Redux Saga可以攔截Redux Actions,並註入自己的功能。

這裡有一些概念需要掌握,以下是您想要牢牢記住的主要關鍵詞:佐賀發電機中間件承諾暫停恢復影響派遣行動完成解決屈服屈服

一種佐賀是一些“故事”,對影響您的代碼造成的。這可能包含我們之前討論過的事情之一,例如HTTP請求或某些保存到緩存的過程。

我們創建一個中間件帶有清單薩加斯運行,可以是一個或多個,然後我們將此中間件連接到Redux存儲。

一種佐賀是一個發電機功能。當一個承諾運行並屈服,中間件暫停佐賀直到承諾解決

一旦承諾解決中間件履歷表傳奇,直到下一個屈服找到陳述,就在那裡暫停再次直到它承諾 解決

在傳奇代碼中,您將生成效果使用由redux-saga包裹。首先,我們可以列出:

  • takeEvery()
  • takeLatest()
  • take()
  • call()
  • put()

當一個影響被執行,佐賀已暫停直到影響完成

例如:

import { takeEvery } from 'redux-saga/effects'

const handleNewMessage = function* handleNewMessage(params) { const socket = new WebSocket(‘ws://localhost:8989’) yield takeEvery(‘ADD_MESSAGE’, (action) => { socket.send(JSON.stringify(action)) }) }

export default handleNewMessage

當。。。的時候中間件執行handleNewMessage佐賀吧停止yield takeEvery指示和等待異步地,當然),直到ADD_MESSAGE動作是派遣。然後,它運行其回調,並且佐賀能夠恢復

基本幫手

輔助程序是底層saga API之上的抽象。

讓我們介紹可用於運行效果的最基本的幫助器:

  • takeEvery()
  • takeLatest()
  • take()
  • put()
  • call()

takeEvery()

takeEvery()在某些示例中使用的是這些幫助者之一。

在代碼中:

import { takeEvery } from 'redux-saga/effects'

function* watchMessages() { yield takeEvery(‘ADD_MESSAGE’, postMessageToServer) }

watchMessages發電機暫停直到ADD_MESSAGE動作射擊,以及每次它會觸發postMessageToServer無限且同時運行(無需postMessageToServer在新的一次可以運行之前終止其執行)

takeLatest()

另一個受歡迎的幫手是takeLatest(),與takeEvery()但只允許一次運行一個函數處理程序,從而避免了並發性。如果在處理程序仍在運行時觸發了另一個操作,它將取消該操作,並使用最新的可用數據再次運行。

takeEvery(),當指定的操作發生時,生成器將永不停止並繼續運行效果。

take()

take()不同之處在於它僅等待一次。當它等待的動作發生時,promise會解析,並且迭代器會恢復,因此可以繼續進行下一個指令集。

put()

將操作分派到Redux存儲。無需傳遞Redux存儲或對saga的分發操作,您可以使用put()

yield put({ type: 'INCREMENT' })
yield put({ type: "USER_FETCH_SUCCEEDED", data: data })

它返回一個簡單的對象,您可以在測試中輕鬆檢查它(稍後再介紹)。

call()

當您想在一個傳奇中調用某個函數時,可以使用產生一個返回promise的yield普通函數調用來做到這一點:

delay(1000)

但這在測試中效果不佳。反而,call()允許您包裝該函數調用並返回一個易於檢查的對象:

call(delay, 1000)

退貨

{ CALL: {fn: delay, args: [1000]}}

並行運行效果

使用以下命令可以並行運行效果all()race(),它們的工作方式截然不同。

all()

如果你寫

import { call } from 'redux-saga/effects'

const todos = yield call(fetch, ‘/api/todos’) const user = yield call(fetch, ‘/api/user’)

第二fetch()在第一個呼叫成功之前,呼叫將不會執行。

要並行執行它們,請將它們包裝到all()

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

const [todos, user] = yield all([ call(fetch, ‘/api/todos’), call(fetch, ‘/api/user’) ])

all()直到兩個都解決call()返回。

race()

race()不同於all()不等待所有幫助者的電話返回。它只是等待一個人返回,並且完成了。

這是一場比賽,看哪個先完成,然後我們忘了其他參與者。

它通常用於取消永遠運行直到發生某種情況的後台任務:

import { race, call, take } from 'redux-saga/effects'

function* someBackgroundTask() { while(1) { //… } }

yield race([ bgTask: call(someBackgroundTask), cancel: take(‘CANCEL_TASK’) ])

當。。。的時候CANCEL_TASK操作發出後,我們停止了其他將永遠運行的任務。

免費下載我的反應手冊


更多反應教程: