Event Bus Utility (defineBus)
This utility creates a type-safe global event bus using mitt, allowing components or services in your app to communicate via events without needing direct references to each other.
✨ Features
- Type-safe events using TypeScript generics.
- Shared or isolated buses by name.
- Lightweight
mitt.
🧠 Why
Event buses can reduce tight coupling and simplify communication across your app. By wrapping mitt, we ensure:
- Proper deduplication using a name-based cache (
Map<string, Emitter>) - Reusability: multiple calls to
defineBus("myBus")return the same bus instance - Type safety for events
🚀 Usage
1. Basic Usage
Global Bus
const { $bus } = defineBus()
$bus.on("hello", (msg) => console.log(msg))
$bus.emit("hello", "world")Local Bus
These can be great to expose for domain apis, keeping our code modules decoupled.
const { $bus: myBus } = defineBus("myBus")
myBus.on("hello", (msg) => console.log(msg))
myBus.emit("hello", "world")2. Typed Events
interface MyEvents {
login: { userId: string }
logout: void
}
const { $bus } = defineBus<MyEvents>("auth")
$bus.on("login", ({ userId }) => console.log("Logged in:", userId))
$bus.emit("login", { userId: "abc123" })🛠 API
defineBus<TEvents>(name?: string)
Creates or returns a cached event bus.
Parameters:
TEvents: A type map of events (Record<EventType, any>)name: Optional. Unique identifier for your bus. Defaults to"."
Returns:
{
$bus: Emitter<TEvents>
}📦 Internals
Internally memoized via
Map<string, Emitter>to avoid re-instantiation.Events are emitted and listened to using the same API as
mitt:.on(event, handler).off(event, handler).emit(event, data)
🧼 Cleanup
Always .off() handlers when done to avoid memory leaks:
const handler = (data) => {}
$bus.on("something", handler)
$bus.off("something", handler)🧪 Testing Tip
In unit tests, call busses.clear() manually to reset state if needed.
Pro tip: Name your buses for scoped communication. No one likes an app where all events are global free-for-alls 😅