PubSub and GenericPubSub
Lightweight reactive event emitter designed for fine-grained UI reactivity and class-based data models.
GenericPubSub<T> is just a generic version of PubSub, for when you need to explicitly define the notifiable keys.
Overview
PubSub implements a minimal publish/subscribe mechanism that allows any class to notify observers when one or more of its properties change.
It’s the core of the reactivity model used in use-less-react, enabling efficient data flow between stores and UI components without redundant renders.
Define a class extending PubSub
import { PubSub } from '@dxbox/use-less-react/classes';
class Counter extends PubSub {
count = 0;
increment() {
this.count++;
// you can use the notify method from PubSub or a decorator (see next)
this.notify('count');
}
}
const counter = new Counter();
Connect the class to a React component via the useReactiveInstance hook
import { useReactiveInstance } from '@dxbox/use-less-react/hooks';
function CounterComponent({ counter }: { counter: Counter }) {
const { state: count } = useReactiveInstance(
counter,
(instance) => instance.count,
['count'],
);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => counter.increment()}>Increment</button>
</div>
);
}
Try it out in the playground:
Notify changes on attributes
counter.notify("count"); // notify single attribute
sprite.notify("x", "y"); // notify multiple attributes
Subscribe to changes
sprite.subscribe((propNames) => {
// do something with the notified propNames, i.e.
if (propNames.includes("x")) {
const x = sprite.x; // retrieve value
// do something with x
}
});
The subscribe method returns all the notified attributes. It doesn't return values, you have to get them manually if you need them.
Use onNotify
sprite.onNotify(["x"], ({ x }) => {
// do something with x
});
The onNotify method returns all the notified attributes in a key-value object.
Notify a derived value calculated via a get method
Let's say you have a class using a get method like this:
export class Sprite extends PubSub {
private x = 0;
private y = 0;
public get position() {
return { x: this.x, y: this.y };
}
@Notifies("position")
public setPosition(x: number, y: number) {
this.x = x;
this.y = y;
}
}
When setPosition notifies position, everything will work like with a standard class attribute. So you can define derived values, notify them, and let useReactiveInstance know their value has changed, like any other attribute.
Declaring reactive properties
Use makeReactiveProperties to automatically define a reactive setter that notifies a property whenever its value is changed.
class MyStore extends PubSub {
counter: number = 0;
message: string = "Hello";
constructor() {
super();
// Declares which properties should receive a reactive setter
this.makeReactiveProperties('counter', 'message');
}
incrementCounter() {
this.counter += 1; // this.notify("counter") is automatically called!
}
}
It's intended to be used inside the constructor, as the last instruction.
It will throw if used on functions:
class MyStore extends PubSub {
counter: number = 0;
constructor() {
super();
this.makeReactiveProperties('incrementCounter');
}
incrementCounter() {
this.counter += 1;
}
}
const store = new MyStore();
will throw Cannot make reactive keys ["incrementCounter"]: functions cannot be made reactive.
Batching notifications
Use batchNotifications for notifying many properties as one single call to notify
await instance.batchNotifications(() => {
someActionThatNotifiesX(); // calls instance.notify("y")
await someOtherActionThatNotifiesY(); // calls instance.notify("y")
if (someCondition) { // if a condition is met
await someActionThatNotifiesZ(); // calls instance.notify("z")
}
});
// at the end of the callback function passed to batchNotifications,
// all the pending notifications are triggered together as instance.notify("x", "y", ...)
Notes
PubSubis agnostic to the UI layer — it can be used in React, Node.js, or any JS runtime.- Use decorators like
@Notifies,@DependsOnand@BatchNotificationsto integrate smoothly with reactivity systems. - Combine with the
@ImmutableClassdecorator for safer state updates in development.