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

React15 - 如何在React 15中实现自定义的事件订阅与发送(例如组件间通信)

在React 15中实现组件间的自定义事件通信,通常采用"发布-订阅"(Pub-Sub)模式。这种模式能让你摆脱繁琐的props层层传递,让任意两个组件直接"对话"。根据你的项目需求,主要有三种实现方式,我按推荐程度逐一介绍。

🎯 方案一:使用专门的库(如mitt)

这是最推荐的方式,特别适合中等以上复杂度的项目。专门的库通常体积小、API友好,并且已经帮你处理好了细节。

  1. 安装:在项目中安装mitt库。

    npm install mitt
    # 或者
    yarn add mitt
    
  2. 创建事件总线:新建一个文件(如 eventBus.js),创建并导出一个mitt实例,它就成为了我们全局的"通信中心"。

    import mitt from "mitt";
    const emitter = mitt();
    export default emitter;
    
  3. 在组件中"订阅"事件(接收方):在需要接收消息的组件中,通常在componentDidMount(类组件)或useEffect(函数组件)中监听事件。关键:在componentWillUnmount中移除监听,防止内存泄漏。

    // 接收方组件
    import React, { useEffect } from "react";
    import emitter from "./eventBus";const ReceiverComponent = () => {useEffect(() => {// 定义处理函数const handleMessage = (data) => {console.log("收到消息:", data);// 更新组件状态或执行其他操作};// 订阅 'message' 事件emitter.on("message", handleMessage);// 组件卸载时取消订阅return () => {emitter.off("message", handleMessage);};}, []);return <div>等待消息...</div>;
    };
    
  4. 在组件中"发布"事件(发送方):在需要发送消息的组件中,通过调用emit方法触发事件,并传递数据。

    // 发送方组件
    import React from "react";
    import emitter from "./eventBus";const SenderComponent = () => {const sendMessage = () => {// 发布 'message' 事件,并携带数据emitter.emit("message", "你好,接收方!");};return <button onClick={sendMessage}>发送消息</button>;
    };
    

🧱 方案二:手写一个简单的Event Bus

如果你希望不引入任何外部依赖,手写一个简单的Event Bus是很好的轻量级方案。

  1. 创建Event Bus工具:新建eventBus.js,实现一个简单的发布-订阅类。

    // eventBus.js
    class EventBus {constructor() {this.events = {}; // 用于存储事件名和对应的回调函数列表}// 订阅事件subscribe(eventName, callback) {if (!this.events[eventName]) {this.events[eventName] = [];}this.events[eventName].push(callback);// 返回一个取消订阅的函数return () => {this.events[eventName] = this.events[eventName].filter((fn) => fn !== callback,);};}// 发布事件publish(eventName, data) {if (this.events[eventName]) {this.events[eventName].forEach((callback) => callback(data));}}
    }const eventBus = new EventBus();
    export default eventBus;
    
  2. 在组件中使用:使用方式与mitt类似,通过eventBus.subscribe监听,eventBus.publish触发。

    // 接收方
    import React, { useEffect } from "react";
    import eventBus from "./eventBus";function Receiver() {useEffect(() => {const unsubscribe = eventBus.subscribe("userLogin", (userData) => {console.log("用户登录:", userData);});return unsubscribe; // 清理订阅}, []);// ...
    }// 发送方
    function Sender() {const login = () => {eventBus.publish("userLogin", { name: "Alice", id: 123 });};// ...
    }
    

🌐 方案三:利用浏览器原生API (EventTarget)

这个方案利用了浏览器内置的EventTarget API和CustomEvent,无需额外安装库,也无需手写复杂逻辑,是一种现代、轻量且原生的方式。

  1. 创建事件总线:直接创建一个EventTarget实例作为中央总线。

    // eventBus.js
    const eventTarget = new EventTarget();
    export default eventTarget;
    
  2. 订阅事件:使用addEventListener监听自定义事件,并通过event.detail获取传递的数据。

    // 接收方组件
    import React, { useEffect } from "react";
    import eventTarget from "./eventBus";const Receiver = () => {useEffect(() => {const handleMessage = (event) => {console.log("收到:", event.detail); // 数据在 detail 属性中};eventTarget.addEventListener("message", handleMessage);return () => {eventTarget.removeEventListener("message", handleMessage);};}, []);// ...
    };
    
  3. 发布事件:使用dispatchEvent方法,并创建一个CustomEvent对象,通过其detail属性携带数据。

    // 发送方组件
    import React from "react";
    import eventTarget from "./eventBus";const Sender = () => {const sendMessage = () => {const event = new CustomEvent("message", {detail: "Hello from EventTarget!",});eventTarget.dispatchEvent(event);};return <button onClick={sendMessage}>发送</button>;
    };
    

💎 总结与建议

以上三种方式都能实现你需要的自定义事件通信。它们如何选择,可以参考这个思路:

方案 优点 缺点 适用场景
mitt库 功能完善,API友好,体积超小(200字节),社区成熟 需要额外安装依赖 推荐用于大多数项目,特别是需要可靠、简洁方案时
手写Event Bus 无依赖,完全掌控,理解原理 需要自己处理边界情况,代码稍多 学习目的,或项目有严格的依赖限制
原生EventTarget 无依赖,利用浏览器内置API,现代、简洁 数据需要放在detail属性里,写法稍显啰嗦 喜欢原生API,不想引入任何库或手写维护代码的场景

另外,无论选择哪种方式,一个重要的原则是:在组件卸载时务必取消订阅,这是避免内存泄漏和诡异bug的关键。

希望这些方案对你有帮助。如果你在实施过程中遇到问题,或者想结合具体的组件场景(比如复杂的嵌套组件通信)再深入聊聊,随时可以问我。

http://www.jsqmd.com/news/519790/

相关文章:

  • MakeBlockDrive驱动库深度解析:硬件抽象与模块化控制
  • 裸机环境下I²C总线-设备分层抽象设计与实现
  • 2026年Q1装修风格如何选?五大耐看高级服务商深度测评 - 2026年企业推荐榜
  • SmolVLA快速部署:GitHub Actions自动化构建smolvla镜像流程
  • Javino协议:嵌入式多智能体机器人串行通信中间件
  • 某讯验证码逆向实战:解密滑块/云验证码/天御/防水墙中的collect、eks、ans等关键参数
  • 手把手教你用ComfyUI Qwen:上传人脸,秒变多风格全身照
  • Minio和Ceph的License详解:AGPLv3 vs LGPL,如何避免商业使用中的法律风险?
  • Arduino声音传感器除了做声控灯,还能玩出什么花样?分享3个创意项目思路
  • RC6红外协议嵌入式库:轻量级C++实现与工程实践
  • Ostrakon-VL-8B赋能微信小程序:图像识别与内容生成实战
  • 2026安徽3+2院校全景调研:发展趋势、头部机构解析与科学择校策略 - 2026年企业推荐榜
  • 基于STM32的温室环境智能监控系统设计
  • 嵌入式C语言面向对象实践与TDD工程方法
  • DevOps05-k8s:Helm【在k8s内进行应用管理】
  • 瑞萨RX MCU在BLDC电机控制中的创新应用与实践
  • 卡尔曼滤波。 1、卡尔曼滤波的含义是现时刻的最佳估计为在前一时刻的最佳估计的基础上根据现时刻的...
  • SUPER COLORIZER模型服务化架构:利用Docker实现一键部署与弹性伸缩
  • STM32白炽灯相位调光系统设计与实现
  • OrCAD17.4原理图DRC设置详解:从入门到精通的避坑指南
  • 10个精简C语言开源项目:嵌入式与系统编程必读范例
  • 【萌新破局CTF】BUUCTF-Basic实战手记:从零到一的解题心路
  • Windows系统下DM8达梦数据库安装全攻略(附优化参数设置)
  • 【告别繁琐传参】MyBatis-Plus 与 PageHelper 的优雅融合之道
  • MusePublic部署ChatGPT竞品模型对比测评
  • SkyWalking 9.7.0与Nacos 1.4.8兼容性实战:SpringBoot 2.7.X环境下的避坑指南
  • YOLO12目标检测模型在零售行业中的智能分析
  • 一文讲透|8个降AI率网站测评:全行业通用降AI率工具深度对比
  • STM32标准库开发:从寄存器操作到外设封装的四级抽象
  • IAR EWSTM8多节点工程配置与实战指南