redux-saga
redux-saga 是 redux 一個中間件,它是基于ES6 的 Generator 功能實作,用于解決異步問題(讓redux中可以直接進行異步操作)。
安裝:
npm i -S redux-saga
項目中使用
store/sagas.js
// saga中間件 主saga,用于差別是否需要saga來處理異步操作,如果沒有異步,則放行
function* mainSaga() {
}
// 監聽saga,監聽type類型為異步操作名稱的,此saga會通過主saga配置設定過來
function* watchSaga() {
}
// 工作saga,監聽saga得到任務後,把任務配置設定給工作saga
function* workSaga() {
}
export default
三步走運作起來:saga
import { createStore,applyMiddleware } from "redux";
import { composeWithDevTools } from '@redux-devtools/extension'
import reducer from "@/store/reducer/index"
import mainSaga from "./sagas";
import createSagaMiddleware from "redux-saga";
const SagaMiddleware = createSagaMiddleware()
const store = createStore(
reducer,
composeWithDevTools(applyMiddleware(SagaMiddleware))
)
// 運作saga
SagaMiddleware.run(mainSaga)
// 導出store
export default
使用saga
擷取資料
let num = useSelector((store)=>{
console.log(store);//擷取資料源
return store.count.num
})
發送dispatch
const dispatch = useDispatch()
const onclickHandler = ()=>{
dispatch({type:"asyncadd",payload:10})
}
- takeEvery 監聽每一次dispatch發送的指令
- put 它是saga提供給我們,用于發送指令給reducer來完成同步操作
- all方法,可以監聽多個監聽saga,它的功能和Promise.all方法一樣,用在子產品化
延遲觸發檢視是否能接受異步操作;
import {takeEvery} from "redux-saga/effects"
function delay(n=3){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('')
},1000*n)
})
}
function* mainSaga() {
yield watchSaga()
}
function* watchSaga() {
yield takeEvery('asyncadd', workSaga)
}
function* workSaga({payload}) {
/*收到發過的的dispatch */
yield delay();//延時函數,看觸發,模拟異步
yield console.log(payload);
yield put({type:"add",payload})
}
export default
狀态源:
const initState = {
num :100
}
export default (state=initState,{type,payload})=>{
if(type == "add"){
return {...state,num:state.num+payload}
}
return state
}
像上面的調用寫法隻能監聽一個saga
這樣的調用,它隻能監聽一個saga,不能進行子產品化
- all方法,可以監聽多個監聽saga,它的功能和Promise.all方法一樣,用在子產品化
import {all} from "redux-saga/effects"
import loginSaga from "./watchsaga/login"
function* mainSaga() {
yield all([
loginSaga()
])
}
export default
拆分這個監聽saga函數
import {takeEvery,put} from "redux-saga/effects"
function delay(n=1){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve('')
},1000*n)
})
}
function* watchSaga() {
yield takeEvery('asyncadd', addSaga)
//可以寫多個
}
function* addSaga({payload}) {
/*收到發過的的dispatch */
yield delay();//延時函數,看觸發,模拟異步,完成網絡請求
yield console.log(payload);
yield put({type:"add",payload})
}
export default
saga進行網絡請求
這裡就有個參數必須得使用:
call方法,調用Promise對象
第一個參數是函數名,而不是去執行函數,後面跟着的就是參數
import {takeEvery,put,call} from "redux-saga/effects"
import {post} from "@/utils/http"
function* watchSaga() {
yield takeEvery('asyncadd', addSaga)
}
/* 在此處完成網絡請求 */
function* addSaga({payload}) {
/*收到發過的的dispatch saga幫我們實作了這個co指派過程*/
let ret = yield call(post,payload)
if(ret.code == 200){
yield put({type:"loginadd",payload:ret.data})//全局更新登入資訊
}
}
export default
實地測試模拟異步的狀态變化,引起視圖的改變。
import {takeEvery,put,call} from "redux-saga/effects"
function delay(n=2){
return new Promise((resolve)=>{
setTimeout(()=>{
resolve({code:200,data:"ok"})
},1000*n)
})
}
function* watchSaga() {
yield takeEvery('asyncadd', addSaga)
}
/* 在此處完成網絡請求 */
function* addSaga({payload}) {
/*收到發過的的dispatch */
let ret = yield call(delay,5)
console.log(ret);
if(ret.code == 200){
yield put({type:"loginadd",payload:{data:ret.data}})//全局更新登入資訊
}
}
export default
兩種跳轉路由的方式:
思路一:hack登入狀态成功,在 修改dispatch之後就把state修改,同步修改之後,直接在redux中擷取判斷,是否需要跳轉。
或者:
進行登入,dispatch是它是一個異步的,交給saga,saga會完成異步操作,通知reducer完成同步修改redux中的state資料改變, reducer把state中的資料修改後,因為我在目前的元件中有通過useEffect來依賴此state中的值的變化,是以它隻要變化了,我就可以來跳轉,進而可以确認redux中的資料一定是存在後才跳轉的
因為:generator的傳回值,不是普通函數這樣的傳回值,這樣在登入成功後,無法讓前端的元件完成路由的切換,切換的原則是登入成功後,才能能跳轉,登入的過程它是一個異步的,是以此時工作就有點難受。說白了,在使用thunk中間件時,異步hook函數useDispatch的dispatch方法之後會有普通函數的傳回值,而使用saga中間件,generator的傳回值就不能使用。
思路二:使用插件
connected-react-router