React状态管理——redux-saga异步操作

目录

一、基础知识

[1.1 生成器函数Generator](#1.1 生成器函数Generator)

[1.2 可执行生成器](#1.2 可执行生成器)

二、redux-saga

[2.1 核心概念](#2.1 核心概念)

[2.1.1 Saga](#2.1.1 Saga)

[2.1.2 Effect](#2.1.2 Effect)

[2.1.3 工作原理](#2.1.3 工作原理)

[2.2 基本使用](#2.2 基本使用)

[2.2.1 安装](#2.2.1 安装)

[2.2.2 目录结构(以多saga、多reducer为例)](#2.2.2 目录结构(以多saga、多reducer为例))

[2.2.3 配置](#2.2.3 配置)

[2.2.3.1 配置reducer](#2.2.3.1 配置reducer)

[2.2.3.2 配置saga](#2.2.3.2 配置saga)

[2.2.3.3 配置store](#2.2.3.3 配置store)

[2.2.4 示例](#2.2.4 示例)

[2.2.4.1 测试代码](#2.2.4.1 测试代码)

[2.2.4.2 运行结果](#2.2.4.2 运行结果)

一、基础知识

1.1 生成器函数Generator

javascript 复制代码
// 前面加*才会成为生成器函数
function* test() {
  console.log(1111);
  var input1 = yield "1111暂停"; // 调用gen.next()会将yield的结果 传给res1
  console.log(2222, input1); //通过gen.next(bbbb)进行传参,可以得到结果,为什么不是aaaa,因为第一次console.log(1111);执行后就停止
  yield "2222暂停";
  console.log(3333);
  yield "3333暂停";
}

let gen = test();

let res1 = gen.next("aaaa"); //调用next方法,遇到yield就停止
let res2 = gen.next("bbbb");
let res3 = gen.next("cccc");
let res4 = gen.next(); // 后面没东西了

console.log(res1); //返回的是个对象{value:...,done:...}
console.log(res2);
console.log(res3);
console.log(res4); // done:true,表示生成器执行完毕

1.2 可执行生成器

javascript 复制代码
function getData1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(["data1"]);
    }, 1000);
  });
}

function getData2(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([...data, "data2"]);
    }, 1000);
  });
}

function getData3(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve([...data, "data3"]);
    }, 1000);
  });
}

function* gen() {
  let f1 = yield getData1();
  console.log(f1);
  let f2 = yield getData2(f1);
  console.log(f2);
  let f3 = yield getData3(f2);
  console.log(f3);
}

function run(fn) {
  let g = fn();

  function next(data) {
    let result = g.next(data);
    if (result.done) {
      return result.value;
    }
    result.value.then((res) => {
      next(res);
    });
  }

  next();
}

run(gen);

二、redux-saga

2.1 核心概念

Redux-Saga 是一个用于管理 Redux 应用异步操作(副作用)的中间件库。它使用 ES6 的 Generator 函数让异步流程更易读、更易测试。

在saga中,全局监听器和接收器使用Generator函数和saga自身的一些辅助函数实现对整个流程的管控

2.1.1 Saga

Saga 是一个 Generator 函数,它使用 yield 关键字来暂停和恢复执行。Saga 负责组织和控制应用的副作用(如数据获取、缓存访问等)。

2.1.2 Effect

Effect 是一个纯 JavaScript 对象,包含了一些将被 saga 中间件执行的指令。常见的 Effect 创建器包括:

  • take: 等待指定的 action

  • put: 发起一个 action

  • call: 调用一个函数(通常是异步的)

  • fork: 非阻塞地执行一个任务

  • all: 并行运行多个 Effect

2.1.3 工作原理

Redux-Saga 作为中间件运行在 Redux 应用中:

  1. 监听特定的 Redux action

  2. 执行相应的副作用逻辑

  3. 可能派发新的 action 来更新 store

2.2 基本使用

2.2.1 安装

bash 复制代码
npm redux redux-saga

2.2.2 目录结构(以多saga、多reducer为例)

在目录结构中,reducer与传统redux配置一致,store/index.js在传统配置中添加了saga,store/saga文件夹下的saga1是基础示例,saga2是应用了链式调用(与 async await相似)

2.2.3 配置

2.2.3.1 配置reducer

目录:store/reducer文件夹

step1:创建reducer

javascript 复制代码
// store/reducer/reducer1

const initState = {
  list1: [],
};

function reducer(prevState = initState, action) {
  let newState = { ...prevState };
  switch (action.type) {
    case "change-list1":
      newState.list1 = action.payload;
      return newState;
    default:
      return newState;
  }
}

export default reducer;
javascript 复制代码
// store/reducer/reducer2

const initState = {
  list2: [],
};

function reducer(prevState = initState, action) {
  let newState = { ...prevState };
  switch (action.type) {
    case "change-list2":
      newState.list2 = action.payload;
      return newState;
    default:
      return newState;
  }
}

export default reducer;

step2:结合多个reducer

javascript 复制代码
// store/reducer/index.js

import { combineReducers } from "redux";
import reducer1 from "./reducer1";
import reducer2 from "./reducer2";

export default combineReducers({
  r1: reducer1, // 前面的名字随便取,只要store.getState().r1对应
  r2: reducer2,
});
2.2.3.2 配置saga

目录:store/saga文件夹

step1:创建saga

javascript 复制代码
// store/saga/saga1.js

import { call, put } from "redux-saga/effects";

function* getList1() {
  // 异步处理
  // call函数发送异步请求 call的传参是个Promise对象
  let res = yield call(getListAction1);
  // put函数发出新的action
  yield put({ type: "change-list1", payload: res });
}

function getListAction1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(["111", "222", "333"]);
    }, 100);
  });
}

export { getList1 };
javascript 复制代码
// store/saga/saga2.js

import { call, put } from "redux-saga/effects";

function* getList2() {
  // 异步处理
  // call函数发送异步请求 call的传参是个Promise对象
  let res1 = yield call(getListAction2_1); // 阻塞调用
  let res2 = yield call(getListAction2_2, res1); // 第二个参数会将结果传参给getListAction2_2
  // put函数发出新的action
  yield put({ type: "change-list2", payload: res2 });
}

function getListAction2_1() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(["444", "555", "666"]);
    }, 1000);
  });
}

function getListAction2_2(data) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([...data, "777", "888", "999"]);
    }, 1000);
  });
}

export { getList2 };

step2:监听多个saga

javascript 复制代码
// store/saga/index.js

import { takeEvery } from "redux-saga/effects";
import { getList1 } from "./saga1";
import { getList2 } from "./saga2";

function* watchSaga() {
  console.log(111);
  yield takeEvery("get-list1", getList1);
  yield takeEvery("get-list2", getList2);
}

export default watchSaga;
2.2.3.3 配置store
javascript 复制代码
// store/index.js

import { createStore, applyMiddleware } from "redux";
import reducer from "./reducer";
import createSagaMidlleWare from "redux-saga";
import watchSaga from "./saga/index";
const sagaMiddleWare = createSagaMidlleWare();
const store = createStore(reducer, applyMiddleware(sagaMiddleWare));

sagaMiddleWare.run(watchSaga); // 运行saga任务
export default store;

2.2.4 示例

2.2.4.1 测试代码
javascript 复制代码
import React from "react";
import store from "./store";
// import "./02-可执行生成器";
export default function App() {
  const fetchClick1 = () => {
    if (store.getState().r1.list1.length === 0) {
      //dispatch
      store.dispatch({ type: "get-list1" });
    } else {
      console.log("缓存1111", store.getState().r1.list1);
    }
  };

  const fetchClick2 = () => {
    if (store.getState().r2.list2.length === 0) {
      //dispatch
      store.dispatch({ type: "get-list2" });
    } else {
      console.log("缓存2222", store.getState().r2.list2);
    }
  };

  return (
    <div>
      <button onClick={fetchClick1}>click-ajax-异步缓存1111</button>
      <button onClick={fetchClick2}>click-ajax-异步缓存2222</button>
    </div>
  );
}
2.2.4.2 运行结果

对按钮进行点击,第1次点击为异步请求,第2次点击为缓存

相关推荐
玺同学23 分钟前
从卡顿到流畅:前端渲染性能深度解析与实战指南
前端·javascript·性能优化
我是谁谁27 分钟前
一篇文章让你学透在Vue 3 中watch 和 watchEffect的区别
javascript
光影少年30 分钟前
vuex中的辅助函数怎样使用
前端·javascript
teeeeeeemo1 小时前
JS数据类型检测方法总结
开发语言·前端·javascript·笔记
木木黄木木1 小时前
HTML5 火焰字体效果教程
前端·html·html5
云墨-款哥的博客1 小时前
失业学习-前端工程化-webpack基础
前端·学习·webpack
MAOX7891 小时前
基于python的web系统界面登录
前端·python
懒大王、1 小时前
Vue添加图片作为水印
前端·javascript·vue.js
Junerver1 小时前
如何在Jetpack Compose中轻松的进行表单验证
前端·kotlin
3Katrina1 小时前
《JavaScript this 指向深度剖析:从基础到复杂场景实战》
前端·javascript