Skip to content

React Hooks

Subscribe to events within React components.

function useSubscribe<TEventName extends EventNames<TEvents>>(
eventName: TEventName,
handler: (payload: EventPayload<TEvents, TEventName>) => void,
deps?: React.DependencyList
): void;
import { useSubscribe } from "./pubsub";
function MyComponent() {
const [messages, setMessages] = useState([]);
useSubscribe("message.received", (payload) => {
setMessages((prev) => [...prev, payload]);
});
return <div>{/* ... */}</div>;
}

Re-subscribe when dependencies change:

function UserMessages({ userId }) {
useSubscribe(
"message.received",
(payload) => {
if (payload.userId === userId) {
// Handle message for this user
}
},
[userId] // Re-subscribe when userId changes
);
}
function ConditionalSubscriber({ enabled }) {
useSubscribe(
"notification",
(payload) => {
console.log("Received:", payload);
},
[enabled],
{ enabled } // Only subscribe when enabled is true
);
}

Get a publish function for sending events.

function usePublish(): <TEventName extends EventNames<TEvents>>(
eventName: TEventName,
payload: EventPayload<TEvents, TEventName>
) => Promise<void>;
import { usePublish } from "./pubsub";
function SendButton() {
const publish = usePublish();
const handleClick = async () => {
await publish("button.clicked", {
buttonId: "send",
timestamp: Date.now(),
});
};
return <button onClick={handleClick}>Send</button>;
}
function SendForm() {
const publish = usePublish();
const [loading, setLoading] = useState(false);
const handleSubmit = async (data) => {
setLoading(true);
try {
await publish("form.submitted", data);
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<button disabled={loading}>
{loading ? "Sending..." : "Submit"}
</button>
</form>
);
}

Monitor the transport connection status.

function useConnectionStatus(): ConnectionState;
// Returns: "disconnected" | "connecting" | "connected" | "reconnecting"
import { useConnectionStatus } from "./pubsub";
function ConnectionBanner() {
const status = useConnectionStatus();
if (status === "connected") return null;
return (
<div className="connection-banner">
{status === "connecting" && "Connecting..."}
{status === "reconnecting" && "Reconnecting..."}
{status === "disconnected" && "Disconnected. Retrying..."}
</div>
);
}

Access the underlying PubSub instance for advanced use cases.

function usePubSub(): {
publisher: Publisher<TEvents>;
subscriber: Subscriber<TEvents>;
transport: Transport;
};
import { usePubSub } from "./pubsub";
function AdvancedComponent() {
const { publisher, subscriber, transport } = usePubSub();
useEffect(() => {
// Direct access to subscriber
const unsub = subscriber.on("custom.event", handler);
return unsub;
}, []);
return <div>{/* ... */}</div>;
}

Build custom hooks on top of PubSubJS hooks:

function useLatestEvent<T extends EventNames<Events>>(eventName: T) {
const [latest, setLatest] = useState<EventPayload<Events, T> | null>(null);
useSubscribe(eventName, (payload) => {
setLatest(payload);
});
return latest;
}
// Usage
function PriceDisplay() {
const price = useLatestEvent("price.updated");
return <span>{price?.value ?? "Loading..."}</span>;
}
function useEventHistory<T extends EventNames<Events>>(
eventName: T,
maxItems = 10
) {
const [history, setHistory] = useState<EventPayload<Events, T>[]>([]);
useSubscribe(eventName, (payload) => {
setHistory((prev) => [...prev.slice(-(maxItems - 1)), payload]);
});
return history;
}
// Usage
function ActivityFeed() {
const activities = useEventHistory("activity.logged", 50);
return (
<ul>
{activities.map((a, i) => (
<li key={i}>{a.description}</li>
))}
</ul>
);
}
function useEventCallback<T extends EventNames<Events>>(eventName: T) {
const publish = usePublish();
return useCallback(
(payload: EventPayload<Events, T>) => {
return publish(eventName, payload);
},
[publish, eventName]
);
}
// Usage
function ChatInput() {
const sendMessage = useEventCallback("chat.message");
const handleSubmit = (text) => {
sendMessage({ text, timestamp: Date.now() });
};
return <input onSubmit={handleSubmit} />;
}
  • Examples - Real-world usage patterns
  • Setup - Configuration options