react18中redux-saga实战系统登录功能及阻塞与非阻塞的性能优化

news2024/11/8 1:55:02

redux-saga中的effect常用的几个是有区分出阻塞与非阻塞的,这里主要看下callfork两者的区别。
在这里插入图片描述
在这里插入图片描述

实现效果

  • 非阻塞的task执行,不用等到登录成功后请求的list接口完成,点击退出按钮可以立即退出
    请添加图片描述
  • 阻塞task的执行,必须等到登录成功后请求的list接口完成,点击退出按钮才有效。下面展示的点击效果,开始点击退出前几次是没有用的,没有反应。
    请添加图片描述
    以上两种方式都有各自使用场景,具体可以根据自己的需求去开发
  • 第一种就是,登录后可以立即退出
  • 第二种就是,登录后会请求列表接口,只有等接口请求完成了,点击退出才会有效。

代码逻辑

  • 页面组件SagaLogin.js
import { Form, Input, Button } from "antd";
import { useState, useEffect } from "react";
import { LoginContainer } from "./style.js";
import { useSelector, useDispatch } from "react-redux";
import { useNavigate } from "react-router-dom";
import * as actionTypes from "../../store/sagas/actionTypes";
function SagaLogin() {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [isLoading, setIsLoading] = useState(false);
  const isLogin = useSelector((state) => state.getIn(["sys", "isLogin"]));
  const navigator = useNavigate();

  const dispatch = useDispatch();
  const handleChangeName = (e) => {
    setUsername(e.target.value);
    dispatch({
      type: actionTypes.CHANGE_USERNAME + "@@SAGA@@",
      value: e.target.value,
    });
  };
  const handleChangePwd = (e) => {
    setPassword(e.target.value);
    dispatch({
      type: actionTypes.CHANGE_PASSWORD + "@@SAGA@@",
      value: e.target.value,
    });
  };

  const onFinish = (values) => {
    console.log(values);
    setIsLoading(true);
    dispatch({ type: actionTypes.LOGIN + "@@SAGA@@", value: values });
  };

  useEffect(() => {
  // 监听登录
    if (isLogin) {
      setIsLoading(false);
      navigator("/saga-list", {
        replace: true,
      });
    }
    return () => {};
  }, [isLogin]);
  return (
    <LoginContainer>
      <div className="main">
        <h2>Saga-Login</h2>

        <br />
        {isLogin}
        <Form onFinish={onFinish}>
          <Form.Item
            label="用户名:"
            name="username"
            rules={[{ required: true, message: "Please input your username!" }]}
          >
            <Input
              placeholder="请输入用户名"
              value={username}
              onChange={handleChangeName}
            />
          </Form.Item>
          <Form.Item
            label="密码:"
            name="password"
            rules={[{ required: true, message: "Please input your password!" }]}
          >
            <Input
              placeholder="请输入密码"
              type="password"
              value={password}
              onChange={handleChangePwd}
            />
          </Form.Item>
          <Form.Item>
            <Button block type="primary" htmlType="submit" loading={isLoading}>
              登录
            </Button>
          </Form.Item>
        </Form>
      </div>
    </LoginContainer>
  );
}

export default SagaLogin;
  • style.js
import styled from "styled-components";
import bg from "./image.png";
export const LoginContainer = styled.div`
  height: 100vh;
  background: url(${bg}) no-repeat center;
  background-size: cover;
  .main {
    background: rgba(255, 255, 255, 0.5);
    height: 100vh;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    h2 {
      margin-bottom: 20px;
    }
    form {
      width: 60%;
    }
  }
`;
  • 跳转列表页面
import { useSelector, useDispatch } from "react-redux";
import { Button, Space, FloatButton } from "antd";
import * as actionTypes from "../../store/sagas/actionTypes";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { AticleContainerContainer } from "./style";
function SagaList() {
  const list = useSelector((state) => state.getIn(["sys", "articleList"]));
  const userInfo = useSelector((state) => state.getIn(["sys", "userInfo"]));
  const isLogin = useSelector((state) => state.getIn(["sys", "isLogin"]));
  const navigator = useNavigate();
  const dispatch = useDispatch();
  const handleLoginOut = () => {
    dispatch({
      type: actionTypes.LOGIN_OUT + "@@SAGA@@",
    });
  };
  useEffect(() => {
   // 监听退出
    if (!isLogin) {
      navigator("/saga-login");
    }
    return () => {};
  }, [isLogin]);
  return (
    <AticleContainerContainer>
      <h1>Saga-List</h1>
      <p>This is the SagaList page.</p>
      <Space>
        <p>{userInfo.get("username")}</p>
        {userInfo.get("password")}
      </Space>
      <div>
        <FloatButton
          type="primary"
          description={"退出"}
          onClick={handleLoginOut}
        >
          退出登录
        </FloatButton>
      </div>

      <ul>
        {list.map((item) => {
          return (
            <li key={item.username}>
              {item.username} --- {item.channelName}
            </li>
          );
        })}
      </ul>
    </AticleContainerContainer>
  );
}
export default SagaList;
  • style.js
import styled from "styled-components";
export const AticleContainerContainer = styled.div`
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
  ul {
    list-style: none;
    li {
      margin: 10px 0;
    }
  }
`;
  • store/index.js
import { createStore, applyMiddleware } from "redux";
import { combineReducers } from "redux-immutable";
import createSagaMiddleware from "redux-saga";
import defAllSags from "./sagas/index.js";
import CounterReducer from "./sagas/counterReducer";
import TaskOkReucer from "./sagas/taskOkReucer.js";
import sysLoginReducer from "./sagas/sysReucers.js";

const sagaMiddleware = createSagaMiddleware();

const reducers = combineReducers({
  count: CounterReducer,
  task: TaskOkReucer,
  sys: sysLoginReducer,  // 系统登录的reducer
});

const store = createStore(reducers, applyMiddleware(sagaMiddleware));

sagaMiddleware.run(defAllSags);

export default store;
  • store/sagas/sysReucers.js
import { fromJS } from "immutable";
import * as actionTypes from "./actionTypes";
const initState = fromJS({
  isLogin: false,
  articleList: [],
  userInfo: {
    username: "",
    password: "",
  },
});

function sysLoginReducer(state = initState, action) {
  console.log("🚀 ~ sysLoginReducer ~ action:", action);
  switch (action.type) {
    case actionTypes.LOGIN_SUCCESS:
      return state.set("isLogin", true);
    case actionTypes.LOGIN_OUT:
      return state.set("isLogin", false);
    case actionTypes.CHANGE_USERNAME:
      return state.mergeIn(["userInfo"], { username: action.value });
    case actionTypes.CHANGE_PASSWORD:
      return state.mergeIn(["userInfo"], { password: action.value });
    case actionTypes.UPDATA_LIST:
      return state.set("articleList", action.value);
    default:
      return state;
  }
}
export default sysLoginReducer;
  • store/sagas/sysSaga.js
import * as actionTypes from "./actionTypes";
import {
  takeLatest,
  fork,
  all,
  delay,
  put,
  call,
  takeEvery,
} from "redux-saga/effects";
// 修改名称
function* handleChangeName(action) {
  console.log("change name", action);
  // yield delay(2000);
  yield put({ type: actionTypes.CHANGE_USERNAME, value: action.value });
}

function* watchChangeName() {
  yield takeLatest(actionTypes.CHANGE_USERNAME + "@@SAGA@@", handleChangeName);
}

function* handleChangePwd(action) {
  console.log("change pwd", action);
  // yield delay(2000);
  yield put({ type: actionTypes.CHANGE_PASSWORD, value: action.value });
}

function* watchChangePwd(action) {
  console.log("change pwd", action);
  yield takeLatest(actionTypes.CHANGE_PASSWORD + "@@SAGA@@", handleChangePwd);
}

function* getList() {
  try {
    yield delay(3000);
    const activityList = [
      { username: "username1", channelName: "channelName1" },
      { username: "username2", channelName: "channelName2" },
      { username: "username3", channelName: "channelName3" },
      { username: "username4", channelName: "channelName4" },
    ];
    yield put({ type: actionTypes.UPDATA_LIST, value: activityList });
  } catch (error) {
    yield put({ type: "update_list_error", error });
  }
}

function* handleLogout() {
  console.log("logout");
  yield put({ type: actionTypes.LOGIN_OUT });
}

function* handleLogin(action) {
  yield delay(2000);
  if (action.value.username === "admin" && action.value.password === "123456") {
    yield put({ type: actionTypes.LOGIN_SUCCESS });
    // 获取列表数据
    yield call(getList);
  } else {
    yield put({ type: actionTypes.LOGIN_FAIL });
  }

  // 监听登出
  yield takeEvery(actionTypes.LOGIN_OUT + "@@SAGA@@", handleLogout);
}
function* watchLogin(action) {
  console.log("login", action);
  yield takeLatest(actionTypes.LOGIN + "@@SAGA@@", handleLogin);
}

function* sysSaga() {
  console.log("SYS SAGA");
  // 监听所有的dispatch
  yield all([fork(watchChangeName), fork(watchChangePwd), fork(watchLogin)]);
}
export default sysSaga;

handleLogin中,我们模拟判断账户密码,触发登录成功更改登录状态,在去请求getList,这个是阻塞的。
这样一看没有什么问题,但是注意call方法调用是会阻塞主线程的,具体来说:

  • call方法调用结束之前,call方法之后的语句是无法执行的
  • 如果call(getList)存在延迟,call(getList)之后的语句 yield takeEvery(actionTypes.LOGIN_OUT + "@@SAGA@@", handleLogout);在call方法返回结果之前无法执行
  • 在延迟期间的登出操作会被忽略。

在这里插入图片描述
无阻塞调用
yield call(getList)改为 yield fork(getList),通过fork方法不会阻塞主线程,在getlist接口未返回结果前点击登出,可以立刻响应登出功能,从而返回登陆页面。

  • store/sagas/actionTypes.js
export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";
export const REQUEST_FAIL = "REQUEST_FAIL";
export const CHANGE_NAME = "CHANGE_NAME";
export const LOGIN_SUCCESS = "LOGIN_SUCCESS";
export const LOGIN = "LOGIN";
export const LOGIN_FAIL = "LOGIN_FAIL";
export const CHANGE_USERNAME = "CHANGE_USERNAME";
export const CHANGE_PASSWORD = "CHANGE_PASSWORD";
export const UPDATA_LIST = "UPDATA_LIST";
export const LOGIN_OUT = "LOGIN_OUT";

总结

通过前面的案例分析,我们可以概括出redux-saga做为redux中间件的全部优点:

  • 统一action的形式,在redux-saga中,从UI中dispatchaction为原始对象
  • 集中处理异步等存在副作用的逻辑
  • 通过转化effects函数,可以方便进行单元测试
  • 完善和严谨的流程控制,可以较为清晰的控制复杂的逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2228387.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JavaEE】【多线程】进阶知识

目录 一、常见的锁策略1.1 悲观锁 vs 乐观锁1.2 重量级锁 vs 轻量级锁1.3 挂起等待锁 vs 自旋锁1.4 普通互斥锁 vs 读写锁1.5 可重入锁 vs 不可重入锁1.6 不公平锁 vs 公平锁 二、synchronized特性2.1 synchronized的锁策略2.2 synchronized加锁过程2.3 其它优化措施 三、CAS3.…

炫酷的登录框!(附源码)

大家想看什么前端效果请留言 预览效果 源码 <!DOCTYPE html> <html lang"zh"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>登录页…

RK3568平台开发系列讲解(内存篇)ioremap进行物理内存的映射

🚀返回专栏总目录 文章目录 一、使用案例:二、ioremap 函数的使用三、volatile 的使用一、使用案例: ioremapdevm_ioremap (你不用担心资源的释放)二、ioremap 函数的使用

中小企业设备资源优化:Spring Boot系统实现

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

基于SSM(spring+springmvc+mybatis)+MySQL开发的新闻推荐系统

基于内容的新闻推荐系统 一、环境搭建 开发框架&#xff1a;SSM(springspringmvcmybatis)开发语言&#xff1a;Java、HTML5、JavaScript开发工具&#xff1a;MyEclipse软件依赖&#xff1a;tomcat8、MySQL 1.1 新建工程 打开 myeclispe&#xff0c;新建一个 maven 工程&…

STM32 HAL ADC FIR 窗函数实现低通滤波

FIR 窗函数实现低通滤波 文章目录 FIR 窗函数实现低通滤波1.窗的分类与选择2.matlab设计3.代码编写4.结果欣赏与群延迟5.其他可能报错5.1.adc采集的数据没问题&#xff0c;vofa打印成-nan5.2.vofa打印的滤波数据为初始化的0 6.代码备忘 1.窗的分类与选择 2.matlab设计 主页命令…

例程学习(学习笔记)

project括号里面表示工程的名称&#xff0c;决定了最后生成的bin文件的名称&#xff0c;比如我们现在编译后的bin文件名称就是hello_world.bin文件&#xff0c;如果改成了project(xxx)&#xff0c;编译后的名称就是xxx.bin 这个文件用来设置路径&#xff0c;咱们得例程案例里面…

uniapp:上拉加载更多、下拉刷新、页面滚动到指定位置

提醒 本文实例是使用uniapp进行开发演示的。 一、需求场景 在开发商品&#xff08;SKU&#xff09;列表页面时&#xff0c;通常有三个需求&#xff1a; 页面下拉刷新&#xff0c;第一页展示最新数据&#xff1b;上拉加载更多数据&#xff1b;列表页面可以滚动到指定位置&#x…

5G在汽车零部件行业的应用

5G技术在汽车零部件行业的应用正在不断深入&#xff0c;为行业的智能化、自动化和高效化转型提供了强大的技术支持。 1、5G技术特点与优势 5G技术具有高速度、低延迟、大连接和切片技术等特点与优势。这些特性为汽车零部件行业提供了稳定、可靠、高效的通信连接&#xff0c;使…

jmeter的基本使用

Jmeter基本使用 一、变量 1.用户定义变量 2.用户参数 二、函数 1.计数器${__counter(,)} 2.时间函数 3.加密函数${__digest(,,,,)} 4. 整数相加${__intSum(,,)} 5.属性函数&#xff0c;${__P(,)}、${__property(,,)}、${__setProperty(,,)} 6.V函数 三、获取响应数据…

算法的学习笔记—和为 S 的连续正数序列(牛客JZ74)

&#x1f600;前言 在牛客网的《剑指 Offer》系列题中&#xff0c;有一道关于输出和为给定值 S 的连续正数序列的问题。这是一道典型的双指针问题&#xff0c;考察我们对连续数列求和的理解和双指针的应用。本文将详细解析这道题的思路&#xff0c;并展示如何实现代码。 &#…

D53【python 接口自动化学习】- python基础之模块与标准库

day53 自定义模块 学习日期&#xff1a;20241030 学习目标&#xff1a;模块与标准库 -- 67 自定义模块&#xff1a;如何编写一个完整功能&#xff1f; 学习笔记&#xff1a; 创建自定义模块 自定义模块注意事项 自定义模块 def func1():return this is a functionclass Cl…

上市公司企业数字金融认知数据集(2001-2023年)

一、测算方式&#xff1a;参考C刊《经济学家》王诗卉&#xff08;2021&#xff09;老师的做法&#xff0c;数字金融认知使用每万字年报描述中包含的对数字金融相关关键词的提及次数&#xff0c;关键词为&#xff1a;互联网、数字化、智能、大数据、电子银行、金融科技、科技金融…

4.2-7 运行MR应用:词频统计

文章目录 1. 准备数据文件2. 文件上传到HDFS指定目录2.1 创建HDFS目录2.2 上传文件到HDFS2.3 查看上传的文件 3. 运行词频统计程序的jar包3.1 查看Hadoop自带示例jar包3.2 运行示例jar包里的词频统计 4. 查看词频统计结果5. 在HDFS集群UI界面查看结果文件6. 在YARN集群UI界面查…

How to Train Neural Networks for Flare Removal

Abstract 当相机指向强光源时&#xff0c;生成的照片可能包含镜头眩光伪影。 耀斑以多种形式出现&#xff08;光晕、条纹、渗色、雾霾等&#xff09;&#xff0c;这种外观的多样性使得去除耀斑变得具有挑战性。 现有的分析解决方案对伪影的几何形状或亮度做出了强有力的假设&a…

Kafka如何控制消费的位置?

大家好&#xff0c;我是锋哥。今天分享关于【Kafka如何控制消费的位置?】面试题&#xff1f;希望对大家有帮助&#xff1b; Kafka如何控制消费的位置? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Kafka 中&#xff0c;控制消费位置主要通过以下几个机制来实…

shell脚本实例(4)while实现1+...+100,linux新增用户

while实现1到100求和 #!/bin/bash/ s0 i1 #-le小于等于 while [ $i -le 100 ] dos$[ $s$i ]i$[ $i1 ] done echo $s echo $i 执行结果如下 修改用户名密码脚本 #!/bin/bash/ #提示用户输入用户名 read -p "请输入用户名&#xff1a;"username useradd $username #提…

Qt 实战(10)模型视图 | 10.5、代理

文章目录 一、代理1、简介2、自定义代理 前言&#xff1a; 在Qt的模型/视图&#xff08;Model/View&#xff09;框架中&#xff0c;代理&#xff08;Delegate&#xff09;是一个非常重要的概念。它充当了模型和视图之间的桥梁&#xff0c;负责数据的显示和编辑。代理可以自定义…

lenovo联想小新 潮7000-14AST(81GE)笔记本原厂Win10系统镜像安装包下载

适用机型&#xff1a;【81GE】 链接&#xff1a;https://pan.baidu.com/s/1ciGya7OjTN73rHFJs52WpQ?pwdkgk4 提取码&#xff1a;kgk4 联想原装出厂系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、Office办公软件、联想电脑管家、联想浏览器…

C语言部分输入输出(printf函数与scanf函数,getchar与putchar详解,使用Linux ubuntu)

1.输入输出 1.1.按格式输入输出 printf 可以在man手册中查看 int printf(const char *format, ...); printf:函数名(参数)int:函数的返回值 功能&#xff1a;按格式在终端输出 参数&#xff1a;多参 返回值&#xff1a;输出字符个数 格式&#xff1a; %d int %c char…