














在React 15中实现组件间的自定义事件通信,通常采用"发布-订阅"(Pub-Sub)模式。这种模式能让你摆脱繁琐的props层层传递,让任意两个组件直接"对话"。根据你的项目需求,主要有三种实现方式,我按推荐程度逐一介绍。
这是最推荐的方式,特别适合中等以上复杂度的项目。专门的库通常体积小、API友好,并且已经帮你处理好了细节。
安装:在项目中安装mitt库。
npm install mitt
# 或者
yarn add mitt
创建事件总线:新建一个文件(如 eventBus.js),创建并导出一个mitt实例,它就成为了我们全局的"通信中心"。
import mitt from "mitt";
const emitter = mitt();
export default emitter;
在组件中"订阅"事件(接收方):在需要接收消息的组件中,通常在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>;
};
在组件中"发布"事件(发送方):在需要发送消息的组件中,通过调用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工具:新建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;
在组件中使用:使用方式与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 });
};
// ...
}
这个方案利用了浏览器内置的EventTarget API和CustomEvent,无需额外安装库,也无需手写复杂逻辑,是一种现代、轻量且原生的方式。
创建事件总线:直接创建一个EventTarget实例作为中央总线。
// eventBus.js
const eventTarget = new EventTarget();
export default eventTarget;
订阅事件:使用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);
};
}, []);
// ...
};
发布事件:使用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的关键。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。