React Redux 中触发异步副作用

news2025/1/18 17:15:55

React Redux 中触发异步副作用

一些基本的配置(这里使用 toolkit)可以在这篇笔记中找到:react-redux 使用小结,这里不多赘述。

触发副作用主流的操作方式有两种:

  1. 组件内操作

    适合只会在当前组件中触发的 API 操作

  2. 写一个 action creator 进行操作

    适合跨组件操作

二者并没有谁好谁坏的区别,主要还是依赖于业务需求

组件内触发

cart slice 的实现如下:

import { createSlice } from '@reduxjs/toolkit';
import { uiActions } from './ui-slice';

const cartSlice = createSlice({
  name: 'cart',
  initialState: {
    items: [],
    totalQuantity: 0,
    totalAmount: 0,
  },
  reducers: {
    addItemToCart(state, action) {
      const newItem = action.payload;
      const existingItem = state.items.find((item) => item.id === newItem.id);
      state.totalQuantity++;
      if (!existingItem) {
        state.items.push({
          id: newItem.id,
          price: newItem.price,
          quantity: 1,
          total: newItem.price,
          title: newItem.title,
        });
        return;
      }

      existingItem.quantity++;
      existingItem.total += existingItem.price;
    },
    removeItemFromCart(state, action) {
      state.totalQuantity--;
      const id = action.payload;
      const existingItem = state.items.find((item) => item.id === id);
      if (existingItem.quantity === 1) {
        state.items = state.items.filter((item) => item.id !== id);
        return;
      }
      existingItem.quantity--;
      existingItem.total -= existingItem.price;
    },
  },
});

export const cartActions = cartSlice.actions;

export default cartSlice;

这个就相当于一个比较基本的购物车功能

UIslice:

import { createSlice } from '@reduxjs/toolkit';

const uiSlice = createSlice({
  name: 'ui',
  initialState: {
    cartIsVisible: false,
    notification: null,
  },
  reducers: {
    toggle(state) {
      state.cartIsVisible = !state.cartIsVisible;
    },
    showNotification(state, action) {
      state.notification = {
        status: action.payload.status,
        title: action.payload.title,
        message: action.payload.message,
      };
    },
  },
});

export const uiActions = uiSlice.actions;

export default uiSlice;

这就是一些 UI 相关的操作。

app.js:

import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import Cart from './components/Cart/Cart';
import Layout from './components/Layout/Layout';
import Products from './components/Shop/Products';
import Notification from './components/UI/Notification';
import { uiActions } from './store/ui-slice';

// to avoid empty data being sent during initial mount
let isInitial = true;

function App() {
  const dispatch = useDispatch();
  const { cartIsVisible, notification } = useSelector((state) => state.ui);
  const cart = useSelector((state) => state.cart);

  useEffect(() => {
    const sendCartData = async () => {
      dispatch(
        uiActions.showNotification({
          status: 'pending',
          title: 'sending',
          message: 'Sending cart data',
        })
      );
      const res = await fetch('some api here', {
        method: 'PUT',
        body: JSON.stringify(cart),
      });

      if (!res.ok) {
        throw new Error('Sending cart data failed.');
      }

      dispatch(
        uiActions.showNotification({
          status: 'success',
          title: 'Success...',
          message: 'Sent cart data successfully.',
        })
      );
    };

    if (isInitial) {
      isInitial = false;
      return;
    }

    sendCartData().catch((error) => {
      dispatch(
        uiActions.showNotification({
          status: 'error',
          title: 'Error...',
          message: 'Sending cart data failed.',
        })
      );
    });
  }, [cart, dispatch]);

  return (
    <>
      {notification && (
        <Notification
          status={notification.status}
          title={notification.title}
          message={notification.message}
        />
      )}
      <Layout>
        {cartIsVisible && <Cart />}
        <Products />
      </Layout>
    </>
  );
}

export default App;

实现上来说如下:

在这里插入图片描述

这里的一些流程:

|- useEffect
|   |- sendCartData(async call)
|   |   |- multiple dispatches

这个就是比较直接的操作,即在 useEffect 中调用异步操作,并且在 thenable 中进行结果的处理(这里就是 redux 的触发)。

custom action creator

之前直接利用 redux toolkit 写的 action 如下:

在这里插入图片描述

这里就是在 slice 外写了一个 customer action creator,也就是一个 thunk。实现方式,就像是在 toolkit 出来之前就要手动写很多的 action 这种感觉。

thunk 的定义如下:

a function that delays an action until later

一个延迟触发 action 的函数

当然,thunk 之类的生态圈已经发展了很多年了(在 toolkit 之前就有了),比如说比较老牌的 Redux Thunk,相对而言比较新一些的 redux-saga,它们都已经在市面上运行的比较稳定,而且周下载量都很大:

在这里插入图片描述

![在这里插入图片描述](https://img-blog.csdnimg.cn/fc54633351ef4525afbc715d51d82df5

很多时候可以根据业务需求进行配置,比如说项目比较简单,又没有现成的脚手架,那么现成的 toolkit 的功能说不定就够了。如果业务需求比较复杂,那么可以考虑使用 thunk 或是 saga。

这里因为是对于购物车的操作,所以 custom action creator 会放在 cart slice 中:

import { createSlice } from '@reduxjs/toolkit';
import { uiActions } from './ui-slice';

const cartSlice = createSlice({
  name: 'cart',
  initialState: {
    items: [],
    totalQuantity: 0,
    totalAmount: 0,
  },
  reducers: {
    addItemToCart(state, action) {
      const newItem = action.payload;
      const existingItem = state.items.find((item) => item.id === newItem.id);
      state.totalQuantity++;
      if (!existingItem) {
        state.items.push({
          id: newItem.id,
          price: newItem.price,
          quantity: 1,
          total: newItem.price,
          title: newItem.title,
        });
        return;
      }

      existingItem.quantity++;
      existingItem.total += existingItem.price;
    },
    removeItemFromCart(state, action) {
      state.totalQuantity--;
      const id = action.payload;
      const existingItem = state.items.find((item) => item.id === id);
      if (existingItem.quantity === 1) {
        state.items = state.items.filter((item) => item.id !== id);
        return;
      }
      existingItem.quantity--;
      existingItem.total -= existingItem.price;
    },
  },
});

// custom action creator
export const sendCartData = (cart) => {
  return async (dispatch) => {
    dispatch(
      uiActions.showNotification({
        status: 'pending',
        title: 'sending',
        message: 'Sending cart data',
      })
    );

    const sendRequest = async () => {
      const res = await fetch('some api here', {
        method: 'PUT',
        body: JSON.stringify(cart),
      });

      if (!res.ok) {
        throw new Error('Sending cart data failed.');
      }
    };

    try {
      await sendRequest();
      dispatch(
        uiActions.showNotification({
          status: 'success',
          title: 'Success...',
          message: 'Sent cart data successfully.',
        })
      );
    } catch (e) {
      dispatch(
        uiActions.showNotification({
          status: 'error',
          title: 'Error...',
          message: 'Sending cart data failed.',
        })
      );
    }
  };
};

export const cartActions = cartSlice.actions;

export default cartSlice;

app.js 中的代码:

import { useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import Cart from './components/Cart/Cart';
import Layout from './components/Layout/Layout';
import Products from './components/Shop/Products';
import Notification from './components/UI/Notification';
import { sendCartData } from './store/cart-slice';

let isInitial = true;

function App() {
  const dispatch = useDispatch();
  const { cartIsVisible, notification } = useSelector((state) => state.ui);
  const cart = useSelector((state) => state.cart);

  useEffect(() => {
    if (isInitial) {
      isInitial = false;
      return;
    }

    dispatch(sendCartData(cart));
  }, [cart, dispatch]);

  return (
    <>
      {notification && (
        <Notification
          status={notification.status}
          title={notification.title}
          message={notification.message}
        />
      )}
      <Layout>
        {cartIsVisible && <Cart />}
        <Products />
      </Layout>
    </>
  );
}

export default App;

可以看到,本质上来说,custom action creator 中的代码基本上就是将组件内部的代码移到了另一个地方进行中心化处理。这样的优点比较多,比如说 e-commerce 的项目来说,在每个商品详情页面中可以进行购物车的操作,也可以单独到购物车的页面进行操作,或是鼠标悬浮到购物车,都会弹出一个下拉框进行简单的操作等。

这样粗略一算就会有 3 个地方都会用到同样的 API 调用,同样的 UI 提示,这个情况下就可以考虑将这部分的代码封装到 custom action creator 中,减少重复代码。

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

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

相关文章

企业数仓DQC数据质量管理实践篇

一.数据质量管理背景 以大数据平台的核心理念是构建于业务之上&#xff0c;用数据为业务创造价值。大数据平台、数据仓库的搭建之初&#xff0c;优先满足业务的使用需求&#xff0c;数据质量往往是被忽视的一环。但随着业务的逐渐稳定&#xff0c;数据质量越来越被人们所重视。…

2.1.3 运算放大器的参数以及选型、静态、交流技术指标

笔者电子信息专业硕士毕业&#xff0c;获得过多次电子设计大赛、大学生智能车、数学建模国奖&#xff0c;现就职于南京某半导体芯片公司&#xff0c;从事硬件研发&#xff0c;电路设计研究。对于学电子的小伙伴&#xff0c;深知入门的不易&#xff0c;特开次博客交流分享经验&a…

基于小脑模型神经网络轨迹跟踪matlab程序

1 CMAC概述 小脑模型神经网络(Cerebellar Model Articulation Controller,CMAC)是一种表达复杂非线性函数的表格查询型自适应神经网络&#xff0c;该网络可通过学习算法改变表格的内容&#xff0c;具有信息分类 存储的能力。 CMAC把系统的输入状态作为一个指针&#xff0c;把相…

Oracle-Autoupgrade方式升级19c

前言: Autoupgrade是Oracle 推出的自动升级工具&#xff0c;通过该工具可以将数据库升级为Oracle12.2之后的版本&#xff0c;工具支持升级前的检查、升级问题修复、一键式自动升级以及升级后的问题修复&#xff0c;极大的简化数据库的升级步骤。 支持的目标升级版本: Oracle D…

用 Java 实现爬虫 (爬取本地html中的人物信息并可视化人物关系)

目录 爬虫简介 常用的工具框架 selenium Jsoup Jsoup介绍 Jsoup的主要功能如下&#xff1a; HTML 相关知识 通过Jsoup元素获取 案例 爬取本地html中的角色信息 HtmlParseUtil 可以利用relation-graph 将人物关系可视化 使用爬虫要注意 查看网站的爬虫协议 爬虫简介…

面试:ANR原因及排查

ANR原因 1、CPU满负荷&#xff0c;I/O阻塞 2、内存不足&#xff0c;系统分配给一个应用的内存是有上限的&#xff0c;长期处于内存紧张&#xff0c;会导致频繁内存交换&#xff0c;进而导致应用的一些操作超时。自己内存泄漏或者其他应用占用的大量内存 3、四大组件ANR 4、…

字符串压缩(一)之ZSTD

一、zstd压缩与解压 ZSTD_compress属于ZSTD的Simple API范畴&#xff0c;只有压缩级别可以设置。 ZSTD_compress函数原型如下&#xff1a; size_t ZSTD_compress(void* dst, size_t dstCapacity, const void* src, size_t srcSize, int compressionLevel) ZSTD_decompress函数原…

Mysql replace into

CREATE TABLE t (id int(11) NOT NULL AUTO_INCREMENT,age int(11) DEFAULT NULL,msg varchar(10) DEFAULT NULL,PRIMARY KEY (id),UNIQUE KEY uniq_age (age) ) ENGINEInnoDB DEFAULT CHARSETutf8;insert into t (age, msg) values (1,aaa),(2,bbb),(3,ccc);id 为自增主键、ag…

「重学JS」你真的懂数据类型吗?

前言 学习了这么久前端&#xff0c;发现自己对于基础知识的掌握并没有那么通透&#xff0c;于是打算重新学一遍JS&#xff0c;引用经济学的一句话&#xff1a;JS基础决定能力高度&#x1f926;&#x1f3fb; 基础很重要&#xff0c;只有基础好才会很少出 bug&#xff0c;大多数…

aws cloudformation 理解常见资源的部署和使用

参考 cfn101-workshopaws cli cloudformation cloudformation是aws的iac工具&#xff0c;以下简称cfn 环境搭建——cfn命令行工具 创建堆栈 aws cloudformation create-stack --stack-name testtemp \--template-body file://testtemp.yaml# --parameters ParameterKeyKey…

二叉树的循环问题

目录 一、二叉树的完全性检验 二、前序遍历的非递归写法 三、中序遍历的非递归写法 四、后序遍历的非递归写法 一、二叉树的完全性检验 给定一个二叉树的 root &#xff0c;确定它是否是一个 完全二叉树 。 在一个 完全二叉树 中&#xff0c;除了最后一个关卡外&#xff0c…

Vue脚手架

脚手架 安装步骤 全局安装vue/cli npm install -g vue/cli 安装之后使用不了vue的命令&#xff0c;查看nodejs文件发现我把vue装在了node_globalnpm这个文件夹中。 解决方法&#xff1a;新增一条path指向该文件夹 切换到你要创建的目录创建脚手架 vue create 项目名称 根据…

[附源码]Python计算机毕业设计Django保护濒危动物公益网站

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

【Hack The Box】linux练习-- Talkative

HTB 学习笔记 【Hack The Box】linux练习-- Talkative &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月27日&#x1f334; &#x…

初始数据结构

目录 1. 集合的框架 集合框架的重要性 数据结构的介绍 算法的介绍 容器背后对应的数据结构 2. 时间复杂度和空间复杂度 算法效率 时间复杂度 时间复杂度的概念 大O的渐进表示法 常见的时间复杂度的计算 空间复杂度 空间复杂度的概念 从本章开始又要开始新的篇章&a…

[附源码]Python计算机毕业设计Django班级事务管理论文2022

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

k8s上部署Harbor通过Nginx-Ingress域名访问

目录 1、k8s集群环境&#xff0c;通过kubesphere安装部署。 1.1 集群基本信息 1.2 集群节点信息 2、安装Harbor 2.1、使用Helm添加Harbor仓库 2.2 、通过openssl生成证书 2.3、 创建secret 2.4、 创建nfs存储目录 2.5、 创建pv 2.6、创建pvc 2.7、values.yaml配置文件 2.…

3-UI自动化-八大元素定位,xpath定位方式和相关的常问面试题

3-UI自动化-八大元素定位&#xff0c;xpath定位方式和相关的常问面试题八大元素定位八大元素定位的使用通过xpath定位xpath语法1. xpath逻辑运算定位2. 层级条件定位3. 索引定位4. 文本定位text()WebElement对象WebElement对象常用属性WebElement对象常用方法find_element()和 …

【Mybatis编程:插入和根据id删除相册数据】

目录 1. Mybatis编程&#xff1a;插入相册数据 2. Mybatis编程&#xff1a;根据id删除相册数据 1. Mybatis编程&#xff1a;插入相册数据 当某个数据表中的id被设计为“自动编号”的&#xff08;auto_increment&#xff09;&#xff0c;在配置<insert>标签时&#xff0…

开心公寓房屋出租管理系统的设计与实现(系统源码+技术文档+论文)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…