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
操作發出後,我們停止了其他將永遠運行的任務。
免費下載我的反應手冊
更多反應教程:
- 一個React簡單的應用示例:通過API獲取GitHub用戶信息
- 用React構建一個簡單的計數器
- 用於React開發的VS Code設置
- 如何通過React Router將道具傳遞給子組件
- 使用Electron和React創建一個應用
- 教程:使用React創建電子表格
- 學習React的路線圖
- 了解如何使用Redux
- JSX入門
- 樣式化的組件
- Redux Saga簡介
- React Router簡介
- React簡介
- 反應組件
- 虛擬DOM
- 反應事件
- 反應狀態
- 反應道具
- 反應片段
- React Context API
- 反應PropTypes
- 反應概念:聲明式
- React:如何在點擊時顯示其他組件
- 如何在React JSX內部循環
- 道具與狀態在React中
- 您應該使用jQuery還是React?
- 使用React需要知道多少JavaScript?
- 蓋茨比介紹
- 如何在React中引用DOM元素
- React中的單向數據流
- 反應高階組件
- 反應生命週期事件
- 反應概念:不變性
- 反應概念:純度
- React鉤子簡介
- create-react-app簡介
- 反應概念:組成
- React:演示組件與容器組件
- React中的代碼拆分
- 使用React進行服務器端渲染
- 如何安裝React
- React中的CSS
- 在React中使用SASS
- 在React中處理表單
- 反應嚴格模式
- 反應門戶
- 反應渲染道具
- 測試React組件
- 如何在React中將參數傳遞給事件處理程序
- 如何處理React中的錯誤
- 如何在JSX中返回多個元素
- React中的條件渲染
- 反應,如何將道具轉移到子組件
- 如何在React中獲取輸入元素的值
- 如何使用useState React鉤子
- 如何使用useCallback React鉤子
- 如何使用useEffect React鉤子
- 如何使用useMemo React鉤子
- 如何使用useRef React鉤子
- 如何使用useContext React鉤子
- 如何使用useReducer React鉤子
- 如何將您的React應用程序連接到相同來源的後端
- 到達路由器教程
- 如何使用React Developer Tools
- 如何學習React
- 如何調試React應用程序
- 如何在React中呈現HTML
- 如何修復`dangerouslySetInnerHTML`與React中的錯誤不匹配
- 我如何解決React登錄表單狀態和瀏覽器自動填充的問題
- 如何在本地主機上的React應用程序中配置HTTPS
- 如何修復React中的“在渲染其他組件時無法更新組件”錯誤
- 我可以在條件內使用React掛鉤嗎?
- 將useState與對像一起使用:如何更新
- 如何使用React和Tailwind在代碼塊中移動
- React,添加到DOM時將焦點放在React中的一個項目上
- 反應,在doubleclick上編輯文本