| name | react-typed-events |
| description | React hooks for @stephansama/typed-events: useListener (single event) and useListeners (event map). Use when registering typed event listeners inside React components that need automatic cleanup on unmount. Import from @stephansama/typed-events/react. Requires react >= 18.
|
| type | framework |
| library | @stephansama/typed-events |
| framework | react |
| library_version | 3.0.9 |
| requires | ["typed-events"] |
| sources | ["stephansama/packages:core/typed-events/src/react.ts","stephansama/packages:core/typed-events/README.md"] |
typed-events — React
This skill builds on typed-events. Read it first for foundational concepts including factory selection, cleanup patterns, and SSR behaviour.
Setup
import * as z from "zod";
import { createBroadcastEvent } from "@stephansama/typed-events";
import { useListeners } from "@stephansama/typed-events/react";
export const appEvents = createBroadcastEvent("my-app", {
update: z.object({ value: z.number() }),
reset: z.object({}),
});
Hooks and Components
useListener — single event
import { useListener } from '@stephansama/typed-events/react';
import { animationEvent } from './events';
function Canvas() {
useListener(animationEvent, ({ data }) => {
draw(data.x, data.y);
});
return <canvas />;
}
Registers the listener on mount and removes it on unmount via useEffect cleanup.
useListeners — multiple events from a map
import { useMemo } from 'react';
import { useListeners } from '@stephansama/typed-events/react';
import { appEvents } from './events';
function Dashboard() {
const handlers = useMemo(() => ({
update: ({ data }: { data: { value: number } }) => setValue(data.value),
reset: () => setValue(0),
}), []);
useListeners(appEvents, handlers);
return <div />;
}
Registers all handlers on mount and removes them all on unmount.
Common Mistakes
HIGH Inline listener object triggers re-registration every render
Wrong:
function MyComponent() {
useListeners(appEvents, {
update: ({ data }) => setValue(data.value),
});
}
Correct:
function MyComponent() {
const handlers = useMemo(
() => ({
update: ({ data }) => setValue(data.value),
}),
[],
);
useListeners(appEvents, handlers);
}
useListeners has [map, listeners] in its useEffect dependency array. An inline object literal is a new reference every render, causing the effect to re-run: the old listener is removed and a new one is registered on every render.
Source: core/typed-events/src/react.ts
HIGH Importing hooks from wrong entrypoint
Wrong:
import { useListener } from "@stephansama/typed-events";
Correct:
import { useListener, useListeners } from "@stephansama/typed-events/react";
React hooks are exported from the /react subpath only. The main entrypoint exports only the event factories.
Source: core/typed-events/package.json exports map
MEDIUM Using useListener with an event map factory
Wrong:
useListener(formEvents, handler);
Correct:
useListeners(formEvents, { submit: handler });
useListener accepts Validator (single-event factories: createEvent, createBroadcastEvent). useListeners accepts ValidatorMap (multi-event factories: createEventMap, createBroadcastChannel, createBroadcastEvent). Note createBroadcastEvent satisfies both interfaces.
Source: core/typed-events/src/react.ts, core/typed-events/src/utils/types.ts