redux-saga的简单使用——saga的模块化拆分——saga进行网络请求——同步修改状态

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 mainSaga

三步走运行起来: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 store

使用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 mainSaga

在这里插入图片描述
状态源:

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 mainSaga

拆分这个监听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 watchSaga

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 watchSaga

实地测试模拟异步的状态变化,引起视图的改变。

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 watchSaga

两种跳转路由的方式:

思路一: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

在接收到异步数据之后, 在redux中成功后的路由跳转。

猜你喜欢

转载自blog.csdn.net/m0_46672781/article/details/127250788