Skip to content

Instantly share code, notes, and snippets.

@amit08255
Created September 22, 2025 16:53
Show Gist options
  • Select an option

  • Save amit08255/400900d47c73dba4a7d835df0fb6a1ef to your computer and use it in GitHub Desktop.

Select an option

Save amit08255/400900d47c73dba4a7d835df0fb6a1ef to your computer and use it in GitHub Desktop.
React App Event Bus Demo
import React, { useEffect, useRef, useState } from "react";
// --- 1. Event Bus Implementation ---
type EventMap = {
"modal:open": { modalId: string };
"modal:close": { modalId: string };
"listing:delete": { listingId: string };
"notification:show": { type: "success" | "error"; message: string };
"dom:action": {
action: string;
element: HTMLElement;
data: Record<string, any>;
};
};
interface AppEvents {
emit<E extends keyof EventMap>(event: E, data: EventMap[E]): Promise<void>;
on<E extends keyof EventMap>(
event: E,
listener: (data: EventMap[E]) => void
): string;
off<E extends keyof EventMap>(event: E, listenerId: string): void;
}
class SimpleAppEvents implements AppEvents {
private listeners: {
[K in keyof EventMap]?: { [id: string]: (data: EventMap[K]) => void };
} = {};
async emit<E extends keyof EventMap>(event: E, data: EventMap[E]) {
const eventListeners = this.listeners[event];
if (eventListeners) {
Object.values(eventListeners).forEach((listener) => listener(data));
}
}
on<E extends keyof EventMap>(
event: E,
listener: (data: EventMap[E]) => void
): string {
const id = Math.random().toString(36).substr(2, 9);
if (!this.listeners[event]) this.listeners[event] = {};
this.listeners[event]![id] = listener;
return id;
}
off<E extends keyof EventMap>(event: E, listenerId: string) {
if (this.listeners[event]) {
delete this.listeners[event]![listenerId];
}
}
}
const appEvents = new SimpleAppEvents();
// --- 2. Modal Handler ---
const modalHandler = {
openModal: (modalId: string) => {
appEvents.emit("notification:show", {
type: "success",
message: `Modal "${modalId}" opened!`,
});
},
closeModal: (modalId: string) => {
appEvents.emit("notification:show", {
type: "success",
message: `Modal "${modalId}" closed!`,
});
},
};
// --- 3. Listing Handler ---
const listingHandler = {
deleteListing: (listingId: string) => {
// Simulate API call
setTimeout(() => {
appEvents.emit("notification:show", {
type: "success",
message: `Listing ${listingId} deleted!`,
});
}, 500);
},
};
// --- 4. Action Dispatcher ---
const actionHandlers = new Map<string, (element: HTMLElement, data: any) => void>();
actionHandlers.set("open-modal", (element, data) => {
if (data.modalId) appEvents.emit("modal:open", { modalId: data.modalId });
});
actionHandlers.set("close-modal", (element, data) => {
if (data.modalId) appEvents.emit("modal:close", { modalId: data.modalId });
});
actionHandlers.set("delete-listing", (element, data) => {
if (data.listingId) appEvents.emit("listing:delete", { listingId: data.listingId });
});
// --- 5. DOM Delegation Hook ---
function useDOMDelegation() {
useEffect(() => {
function parseDataAttributes(element: HTMLElement) {
const data: Record<string, any> = {};
for (const attr of Array.from(element.attributes)) {
if (attr.name.startsWith("data-") && attr.name !== "data-action") {
const key = attr.name
.slice(5)
.replace(/-([a-z])/g, (_, l) => l.toUpperCase());
data[key] = attr.value;
}
}
return data;
}
function delegatedClickHandler(event: Event) {
const target = event.target as HTMLElement;
if (!target) return;
const actionElement = target.closest("[data-action]") as HTMLElement;
if (actionElement) {
const action = actionElement.dataset.action!;
const data = parseDataAttributes(actionElement);
appEvents.emit("dom:action", { action, element: actionElement, data });
}
}
document.addEventListener("click", delegatedClickHandler);
return () => {
document.removeEventListener("click", delegatedClickHandler);
};
}, []);
}
// --- 6. Register Handlers to Event Bus ---
// Modal events
appEvents.on("modal:open", ({ modalId }) => modalHandler.openModal(modalId));
appEvents.on("modal:close", ({ modalId }) => modalHandler.closeModal(modalId));
// Listing events
appEvents.on("listing:delete", ({ listingId }) => listingHandler.deleteListing(listingId));
// DOM action dispatcher
appEvents.on("dom:action", ({ action, element, data }) => {
const handler = actionHandlers.get(action);
if (handler) handler(element, data);
});
// --- 7. Notification Component ---
function NotificationCenter() {
const [notifications, setNotifications] = useState<
{ type: string; message: string; id: number }[]
>([]);
useEffect(() => {
const id = appEvents.on("notification:show", ({ type, message }) => {
setNotifications((prev) => [
...prev,
{ type, message, id: Date.now() + Math.random() },
]);
setTimeout(() => {
setNotifications((prev) => prev.slice(1));
}, 2000);
});
return () => appEvents.off("notification:show", id);
}, []);
return (
<div style={{ position: "fixed", top: 10, right: 10, zIndex: 1000 }}>
{notifications.map((n) => (
<div
key={n.id}
style={{
background: n.type === "success" ? "#d4edda" : "#f8d7da",
color: "#155724",
border: "1px solid #c3e6cb",
borderRadius: 4,
marginBottom: 8,
padding: "8px 16px",
minWidth: 200,
}}
>
{n.message}
</div>
))}
</div>
);
}
// --- 8. Demo UI ---
function DemoTable() {
const [listings, setListings] = useState([
{ id: "101", name: "Listing A" },
{ id: "102", name: "Listing B" },
{ id: "103", name: "Listing C" },
]);
useEffect(() => {
const id = appEvents.on("listing:delete", ({ listingId }) => {
setListings((prev) => prev.filter((l) => l.id !== listingId));
});
return () => appEvents.off("listing:delete", id);
}, []);
return (
<table border={1} cellPadding={8} style={{ marginTop: 16 }}>
<thead>
<tr>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{listings.map((listing) => (
<tr key={listing.id}>
<td>{listing.name}</td>
<td>
<button
data-action="delete-listing"
data-listing-id={listing.id}
style={{ color: "red" }}
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
);
}
function DemoModalTrigger() {
const [modalOpen, setModalOpen] = useState<string | null>(null);
useEffect(() => {
const openId = appEvents.on("modal:open", ({ modalId }) => setModalOpen(modalId));
const closeId = appEvents.on("modal:close", () => setModalOpen(null));
return () => {
appEvents.off("modal:open", openId);
appEvents.off("modal:close", closeId);
};
}, []);
return (
<>
<button
data-action="open-modal"
data-modal-id="createListingModal"
style={{ marginBottom: 16 }}
>
Open Create Listing Modal
</button>
{modalOpen && (
<div
style={{
position: "fixed",
top: 80,
left: "50%",
transform: "translateX(-50%)",
background: "#fff",
border: "1px solid #ccc",
borderRadius: 8,
padding: 24,
zIndex: 100,
minWidth: 300,
}}
>
<h3>{modalOpen}</h3>
<p>This is a modal. You can close it below.</p>
<button data-action="close-modal" data-modal-id={modalOpen}>
Close Modal
</button>
</div>
)}
</>
);
}
// --- 9. Main Demo Component ---
export default function EventBusDemo() {
useDOMDelegation();
return (
<div style={{ fontFamily: "sans-serif", padding: 32 }}>
<h2>Event Bus Demo: Decoupled UI Actions</h2>
<p>
Click the buttons below. All UI actions are handled via a global event bus and a single DOM event listener.
</p>
<DemoModalTrigger />
<DemoTable />
<NotificationCenter />
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment