当前位置: 首页 > news >正文

发布订阅模式的应用:解决react中复杂层级的数据交互

以自定义事件为调度中心,创建一个EventCenter类,默认导出一个EventCenter实例

// 调度中心
class EventCenter {constructor() {// 创建一个事件中心,数据模型:{ event : [fn, fn] }this.eventCenter = {};}/*** 订阅事件* eventName {string} 订阅事件名称* backFn {Function} 收到通知的回调函数**/listen(eventName, backFn) {// 确定是否有订阅过该事件if (!this.eventCenter[eventName]) {this.eventCenter[eventName] = [];}// 控制backFn的唯一性,防止出现重复的backFnconst backFnList = this.eventCenter[eventName];const hasFn = backFnList.some((fn) => fn === backFn);if (hasFn) {return;}backFnList.push(backFn);}/*** 取消订阅函数* eventName {string} 取消订阅的事件名称* backFn {Function} 取消订阅的回调函数**/unListen(eventName, backFn) {// 判断是否有该事件,及该事件是否有订阅者let backFnList = this.eventCenter[eventName];if (!backFnList || backFnList.length === 0) {return;}backFnList = backFnList.filter((fn) => fn !== backFn); // 过滤掉取消订阅的函数}/*** 发布函数* eventName {string} 发布通知的事件名称* ...args {any} 发布通知的相关参数**/publish(eventName, ...args) {// 判断是否有该事件,及该事件是否有订阅者const backFnList = this.eventCenter[eventName];if (!backFnList || backFnList.length === 0) {return;}backFnList.forEach((backFn) => {backFn.apply(null, args); // 调用回调函数,并传入发布时的相关参数});}
}export default new EventCenter(); // 默认导出一个实例

publish中使用了apply是为了把参数进行转换,具体请参考此文章:《javascript 关于bind、apply、call函数改变this指向》

运用场景一: 添加商品与购物车的数量变化,这种情况往往是不知道跨越多少不同层级的情况。
发布者:商品加入购物车的操作
订阅者:购物车的数量

// 商品列表 ProductList.js
import EventCenter from './EventCenter';
const ProductList = ()=>{// 模拟商品数据const productList = [{id:1, name: '商品1'}];const addBuyCar = (product)=>{EventCenter.publish('addBuyCar', product); // 发布通知,添加商品到购物车}return (<ul>{productList.map(product => {return (<li key={product.id}>商品名称:{product.name} <button onClick={()=>addBuyCar(product)}>加入购物车</button></li>)})}</ul>);
}
export default ProductList;
// 购物车商品种类数量 BuyCarCount.js
import {useEffect, useState} from 'react';
import EventCenter from './EventCenter';
const BuyCarCount = ()=>{const [productList, setProductList] = useState([]); // 产品种类列表useEffect(()=>{// 订阅添加商品时的回调函数const backFn = (product)=>{setProductList(pList => {// 判断购物车是否已存在此商品,没有才添加进去const hasProduct = pList.some(pro => pro.id === product.id);if(!hasProduct){return [...pList, product];}return pList;});}EventCenter.listen('addBuyCar', backFn); // 订阅addBuyCar事件return ()=> EventCenter.unListen('addBuyCar', backFn); // 组件销毁后,取消订阅}, []);return <div>购物车商品种类:{productList.map(item=> item.name)}</div>;
}
export default BuyCarCount;

相关文章:

  • Ubuntu18.04安装Matlab流程笔记
  • 使用Win32API实现贪吃蛇小游戏
  • Compose | UI组件(十三) | Navigation - 页面导航
  • Spring 中获取 Bean 对象的三种方式
  • 十分钟上手vue!
  • Elasticsearch:Geoshape query
  • 基于YOLOv8深度学习的水稻叶片病害智能诊断系统【python源码+Pyqt5界面+数据集+训练代码】深度学习实战
  • anaconda+pytorch+pycharm安装总结
  • M1芯片MAC 安装MySQL、Nacos遇到的问题
  • VUE开发记录
  • CTFHub:web-LD_PRELOAD-WP
  • 担心信息泄露被恶意申贷,查大数据报告有用吗?
  • linux中的makefile
  • -1- Python环境安装
  • C++模版初阶
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • Android优雅地处理按钮重复点击
  • Angular2开发踩坑系列-生产环境编译
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • Java|序列化异常StreamCorruptedException的解决方法
  • Javascript Math对象和Date对象常用方法详解
  • Java多态
  • Java知识点总结(JavaIO-打印流)
  • JS笔记四:作用域、变量(函数)提升
  • Node 版本管理
  • Yeoman_Bower_Grunt
  • 机器学习中为什么要做归一化normalization
  • 前言-如何学习区块链
  • 强力优化Rancher k8s中国区的使用体验
  • 数据结构java版之冒泡排序及优化
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 曾刷新两项世界纪录,腾讯优图人脸检测算法 DSFD 正式开源 ...
  • ​ubuntu下安装kvm虚拟机
  • $.ajax()
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (2)MFC+openGL单文档框架glFrame
  • (2015)JS ES6 必知的十个 特性
  • (9)STL算法之逆转旋转
  • (day 12)JavaScript学习笔记(数组3)
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (七)c52学习之旅-中断
  • (原創) 物件導向與老子思想 (OO)
  • (转载)OpenStack Hacker养成指南
  • .net core MVC 通过 Filters 过滤器拦截请求及响应内容
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET LINQ 通常分 Syntax Query 和Syntax Method
  • .net 程序 换成 java,NET程序员如何转行为J2EE之java基础上(9)
  • .NET/C# 的字符串暂存池
  • .NET/C# 使用 #if 和 Conditional 特性来按条件编译代码的不同原理和适用场景
  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • [1] 平面(Plane)图形的生成算法
  • [2019.3.20]BZOJ4573 [Zjoi2016]大森林
  • [Asp.net MVC]Asp.net MVC5系列——Razor语法