redux-saga 是一個(gè)用于管理應(yīng)用程序副作用(例如異步獲取數(shù)據(jù)型凳,訪問(wèn)瀏覽器緩存等)的庫(kù)沼沈,它的目標(biāo)是讓副作用管理更容易流酬,執(zhí)行更高效,測(cè)試更簡(jiǎn)單列另,在處理故障時(shí)更容易芽腾。
你可能已經(jīng)用了 redux-thunk 來(lái)處理數(shù)據(jù)的讀取。不同于 redux thunk页衙,你不會(huì)再遇到回調(diào)地獄了摊滔,你可以很容易地測(cè)試異步流程并保持你的 action
是干凈的。
// action.js
import * as types from './types'
export default {
increment(count) {
return {
type: types.INCREMENT,
payload: count
}
}
}
Reducers 指定了應(yīng)用狀態(tài)的變化如何響應(yīng) actions 并發(fā)送到 store 的店乐,reducer
就是一個(gè)純函數(shù)艰躺,接收舊的 state
和 action
,返回新的 state
≌0耍現(xiàn)在只需要謹(jǐn)記 reducer 一定要保持純凈腺兴。只要傳入?yún)?shù)相同,返回計(jì)算得到的下一個(gè) state 就一定相同廉侧。沒(méi)有特殊情況页响、沒(méi)有副作用,沒(méi)有 API 請(qǐng)求段誊、沒(méi)有變量修改闰蚕,單純執(zhí)行計(jì)算。
// reducer.js
import * as types from './types'
let initState = { number: 0 }
export default function (
state = initState,
action
) {
switch (action.type) {
case types.INCREMENT:
return {
number: action.payload
}
default:
return state
}
}
// app.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import action from './store/action'
class App extends Component {
add = () => {
// 異步處理
setTimeout(() => {
let count = this.props.number + 1
this.props.increment(count)
}, 1000);
}
render() {
return (
<>
<div>
{this.props.number}
</div>
<button onClick={this.add}>
點(diǎn)擊
</button>
</>
);
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps, action)(App);
異步都寫在組件里面枕扫,處理完成組件都可以直接獲取陪腌,就沒(méi)有必要把數(shù)據(jù)寫到store里面。如果其他地方也要使用異步修改狀態(tài)烟瞧,那么就沒(méi)法復(fù)用诗鸭,現(xiàn)在使用 redux-saga來(lái)解決這個(gè)問(wèn)題。
// sagas.js
import * as types from './types'
import { put, delay, takeEvery } from 'redux-saga/effects'
export function* incrementAsync(action) {
yield delay(1000)
// 使用這個(gè)函數(shù)去阻塞 Generator
yield put({
type: types.INCREMENT,
payload: action.payload
})
// 然后 dispatch 一個(gè)叫 INCREMENT 的 action
}
export function* watchIncrementAsync() {
//用于監(jiān)聽(tīng)所有的 ASYNC_INCREMENT action
yield takeEvery(
types.ASYNC_INCREMENT,
incrementAsync
)
}
export function* rootSaga() {
yield watchIncrementAsync()
}
takeEvery
参滴,用于監(jiān)聽(tīng)所有的 ASYNC_INCREMENT
action强岸,并在 action 被匹配時(shí)執(zhí)行 incrementAsync
任務(wù)。incrementAsync
Saga 通過(guò) delay(1000)
延遲了 1 秒鐘砾赔,然后 dispatch 一個(gè)叫 INCREMENT
的 action蝌箍。
// action.js
import * as types from './types'
export default {
increment(count) {
return {
type: types.INCREMENT,
payload: count
}
},
// 添加一個(gè) ASYNC_INCREMENT 類型的 action
asyncIncrement(count) {
return {
type: types.ASYNC_INCREMENT,
payload: count
}
}
}
首先我們引入 ./sagas
模塊中的 Saga。然后使用 redux-saga 模塊的 createSagaMiddleware
工廠函數(shù)來(lái)創(chuàng)建一個(gè) Saga middleware暴心。我們必須使用 applyMiddleware
將 middleware 連接至 Store妓盲。然后使用 sagaMiddleware.run(helloSaga)
運(yùn)行 Saga。
import { createStore, applyMiddleware } from 'redux'
import reducer from './reducer'
import createSagaMiddleware from 'redux-saga'
import { rootSaga } from './sagas'
const sagaMiddleware = createSagaMiddleware()
let store = applyMiddleware(sagaMiddleware)(createStore)(reducer)
sagaMiddleware.run(rootSaga)
export default store
import React, { Component } from 'react';
import { connect } from 'react-redux'
import action from './store/action'
class App extends Component {
asyncAdd = () => {
// dispatch 一個(gè)叫 ASYNC_INCREMENT 的 action专普,一旦提交會(huì)執(zhí)行 saga 中的 incrementAsync 函數(shù)
let count = this.props.number + 1
this.props.asyncIncrement(count)
}
render() {
return (
<>
<div>
{ this.props.number }
</div>
<button onClick={this.asyncAdd}>
點(diǎn)擊
</button>
</>
);
}
}
const mapStateToProps = (state) => {
return state
}
export default connect(mapStateToProps, action)(App);
通過(guò)把一些異步代碼都封裝到 saga 里面悯衬,這樣更好的管理代碼,提高了復(fù)用效率檀夹,比如可以在 saga 里面發(fā)送API請(qǐng)求獲取數(shù)據(jù)等等筋粗。