Cross-Component Communication
While not the trendiest tool in modern frontend circles, the EventBus remains one of the most underrated solutions for cross-component communication. Jadis embraces this pattern with intention, offering a built-in Bus system that’s not only easy to use, but also memory-safe and fully typed for TypeScript and JSDoc users.
Jadis brings you a bus helper for handling events in a type-safe manner. Even in plain JavaScript, Jadis allows for partial typing by defining event payloads using primitive constructors like String, Number or other constructors. This gives you lightweight type hints without switching languages, making event wiring safer and more expressive, even in a pure JS project.
You don’t need to reinvent global coordination. With Jadis, you get all the power of a scalable event system, minus the complexity.
Bus creation
Here's how to create a new event bus:
const myBus = new Bus({
someEvent: String,
anotherEvent: Number,
noPayloadEvent: undefined,
});const myBus = new Bus<{
someEvent: string;
anotherEvent: number;
noPayloadEvent: undefined;
}>();/** @type {Bus<{ someEvent: string, anotherEvent: number, noPayloadEvent: undefined }>} */
const myBus = new Bus();When using plain JavaScript, event types are declared using primitive constructors like Number, String, Boolean, BigInt, Symbol, Function, Array or Object
Bus Usage
Once the bus is defined, you can register listeners or emit events freely between components.
/// <reference types="@jadis/core/jsx-runtime" />
/** @jsxImportSource @jadis/core */
import { Bus, Jadis, createSelector } from '@jadis/core';
const myBus = new Bus({
someEvent: String,
anotherEvent: Number,
noPayloadEvent: undefined,
});
class ReceiverComponent extends Jadis {
static selector = createSelector('receiver-component');
templateHtml() {
return <p></p>;
}
onConnect() {
this.onBus(myBus, 'someEvent', (value) => {
this.getElement('p').textContent = `Received: ${value}`;
});
}
}
class EmitterComponent extends Jadis {
static selector = createSelector('emitter-component');
templateHtml() {
return <button>ClickMe</button>;
}
onConnect() {
this.getElement('button').addEventListener('click', () => {
myBus.emit('someEvent', 'Hello from EmitterComponent!');
});
}
}
ReceiverComponent.register();
EmitterComponent.register();import { Bus, Jadis } from '@jadis/core';
const myBus = new Bus<{
someEvent: string;
anotherEvent: number;
noPayloadEvent: undefined;
}>();
class ReceiverComponent extends Jadis {
static readonly selector = 'receiver-component';
templateHtml(): Node {
return <p></p>;
}
onConnect(): void {
this.onBus(myBus, 'someEvent', (value) => {
this.getElement('p').textContent = `Received: ${value}`;
});
}
}
class EmitterComponent extends Jadis {
static readonly selector = 'emitter-component';
templateHtml(): Node {
return <button>ClickMe</button>;
}
onConnect(): void {
this.getElement('button').addEventListener('click', () => {
myBus.emit('someEvent', 'Hello from EmitterComponent!');
});
}
}
ReceiverComponent.register();
EmitterComponent.register();// @ts-check
/// <reference types="@jadis/core/jsx-runtime" />
/** @jsxImportSource @jadis/core */
import { Bus, Jadis, createSelector } from '@jadis/core';
/** @type {Bus<{ someEvent: string, anotherEvent: number, noPayloadEvent: undefined }>} */
const myBus = new Bus();
class ReceiverComponent extends Jadis {
static selector = createSelector('receiver-component');
templateHtml() {
return <p></p>;
}
onConnect() {
this.onBus(myBus, 'someEvent', (value) => {
this.getElement('p').textContent = `Received: ${value}`;
});
}
}
class EmitterComponent extends Jadis {
static selector = createSelector('emitter-component');
templateHtml() {
return <button>ClickMe</button>;
}
onConnect() {
this.getElement('button').addEventListener('click', () => {
myBus.emit('someEvent', 'Hello from EmitterComponent!');
});
}
}
ReceiverComponent.register();
EmitterComponent.register();Usage Outside of Jadis Components
You can also use the bus outside of Jadis components. For example, in services or utilities. If you're registering listeners directly (without Jadis), it's important to provide an AbortSignal to ensure proper cleanup and avoid memory leaks.
// Create an AbortController to manage cleanup
const abortController = new AbortController();
// Register to an event
myBus.register(
'someEvent',
(value) => console.log(`Bus received someEvent with value: ${value}`),
abortController.signal
);
// Emit an event
myBus.emit('someEvent', 'Initial value');
// Clean up when done
abortController.abort();