《告别复杂状态管理:用Mitt实现优雅的React消息通知系统》

118 阅读4分钟

前言

大家好,今天给大家介绍一个项目开发中的必备技能-mitt,注意了!它可不是mit(麻省理工学院)哦😁😁,有了它,能够让我们轻松地完成组件间的订阅者--发布者的通信

从消息组件开始说起

首先大家看一个图:

image.png

这里的右下角的消息组件是我们组件开发中的必备技能,当其它组件进行某些事件(关注,添加好友等),这里的消息组件就会冒出提醒用户,那么问题来了,在React中,我们如何实现这个消息组件的状态通信和管理呢?


如何实现状态通信?

先让我们回忆回忆有哪些状态通信的方法:

  • 父子通信(props)
  • 子父通信(props中的自定义事件)
  • 全局通信(跨层级通信)(useContext)

而对于这里的需求,我们貌似对哪一种通信都不太适合,这时候Mitt就来了,Mitt是一个轻量级(仅200字节)的事件订阅/发布库,适用于React组件间通信。它解决了跨层级组件传值的痛点,替代冗余的props逐层传递或Context的复杂场景,适用于弹窗控制、全局状态更新等场景,是React生态简洁高效的通信方案。


mitt的核心API

核心API

Mitt的API极其简单,主要包含以下方法:

方法说明
mitt()创建一个事件发射器实例
emitter.on(type, handler)订阅事件
emitter.off(type, handler)取消订阅
emitter.emit(type, [data])触发事件
emitter.all获取所有事件监听器(Map结构)

基本使用示例

import mitt from 'mitt';

// 1. 创建实例
const emitter = mitt();

// 2. 订阅事件
emitter.on('sayHello', (name) => {
  console.log(`Hello, ${name}!`);
});

// 3. 触发事件
emitter.emit('sayHello', 'Alice'); // 输出: Hello, Alice!

// 4. 取消订阅
const handler = (name) => console.log(`Bye, ${name}!`);
emitter.on('sayBye', handler);
emitter.off('sayBye', handler); // 移除监听器

在业务中使用mitt

让我们针对刚才的通知组件, 直接在业务中演示mitt的使用吧

1.创建组件结构

首先咱们在components文件夹中,创建组件的结构

src/
  ├── components/
  │   ├── Toast/
  │   │   ├── Toast.jsx      # 组件UI及事件订阅
  │   │   ├── toastController.js  # Mitt事件管理中心
  │   │   └── index.js       # 组件入口

image.png

2.Mitt事件管理中心 (toastController.js)

这里的toastController.js就是mitt的核心 我们在这里面写下:

import mitt from 'mitt';

/**
 * 创建Mitt事件中心
 * 采用单例模式,保证全局唯一事件总线
 */
export const toastEvents = mitt();

/**
 * 封装Toast触发方法
 * @param {number} [user=0] - 用户相关通知数
 * @param {number} [bell=0] - 铃铛通知数
 * @param {number} [mail=0] - 邮件通知数
 */
export function showToast(user = 0, bell = 0, mail = 0) {
  // 发射'show'事件,携带通知数据
  toastEvents.emit('show', { user, bell, mail });
}

首先,我们引入mitt模块并且得到mitt的实例toastEvents,下面这个showToast 是对外暴露的API,业务组件调用此方法即可触发Toast显示,待会你就能直到它的作用啦!

3.为Toast订阅一个事件

我们接下来在Toast.jsx里面添加如下内容:

import { useEffect, useState } from 'react';
import { toastEvents } from './toastController';

function Toast() {
  const [visible, setVisible] = useState(false);
  const [data, setData] = useState({ user: 0, bell: 0, mail: 0 });

  useEffect(() => {
    /**
     * 事件处理函数:显示Toast
     * @param {object} info - 包含通知数据的对象
     */
    const handleShow = (info) => {
      setData(info);          // 更新通知数据
      setVisible(true);       // 显示Toast
      setTimeout(() => setVisible(false), 3000); // 3秒后自动隐藏
    };

    // 订阅'show'事件
    toastEvents.on('show', handleShow);

    // 组件卸载时取消订阅(防止内存泄漏)
    return () => toastEvents.off('show', handleShow);
  }, []);

  return (
     <div className={styles.toastWrapper}>
            <div className={styles.toastItem}>👤 {data.user}</div>
            <div className={styles.toastItem}>🔔 {data.bell}</div>
            <div className={styles.toastItem}>✉ {data.mail}</div>
            <div className={styles.toastArrow}></div>
        </div>
  );
}

这里就是通过刚刚创建的mitt实例,将Toast设置为订阅者,Toast组件通过on方法,监听着事件,当一有发布者发布事件时,它就会立马执行回调函数show()

它有以下功能:

  1. 订阅事件总线的show事件
  2. 管理自身的显示状态
  3. 自动隐藏功能
  4. 完整的生命周期管理,避免内存泄漏
  5. 清晰的数据展示UI

这种实现方式使得Toast组件与其他组件完全解耦,只需要通过事件总线进行通信,大大提高了组件的可复用性。

4.发布者

有了toastController.js里面的showToast,所以任意业务组件中均可触发Toas,我们这里就随便选一个地方触发吧,验证一下效果:

import { showToast } from './components/Toast/toastController';


  return (
    <div className={styles.container}>
        <Button onClick={() => showToast(1,2,3)}> showToast触发</Button>
    </div>
  );
}

在这里我们引用了之前在Mitt事件管理中心 (toastController.js)里面的showToast

而现在,我们就能看到mitt的效果了,在主页点击按钮(模拟消息发送,好友添加),就能看到该消息组件会自动弹出

zc.gif

现在,通过这个Toast案例,我们可以看到Mitt如何优雅地解决React组件通信问题。它特别适合这种短暂、全局、低耦合的交互场景,避免了Redux的臃肿和Context的渲染性能问题。建议在类似通知、弹窗、全局状态更新等场景优先考虑Mitt方案


总结

Mitt是一个极其轻量但强大的事件通信库,特别适合React中的跨组件通信需求。它避免了Redux的复杂性和Context的渲染问题,提供了一种高效、解耦的解决方案。

推荐使用场景
✔ 全局事件(如通知、弹窗控制)
✔ 微前端/模块化应用通信
✔ 简单状态同步(非复杂状态管理)

GitHub地址github.com/developit/m…

如果你正在寻找一个简单、高效的React事件通信方案,Mitt值得一试! 🚀