defineChannel
defineChannel binds a channel’s name and message map once, at module
level, and hands back ready-to-use typed hooks. It’s sugar over
useChannel + useMessage +
useSend for when the trio feels like ceremony: declare the
channel in one file, and every component gets two-line usage with no
generics and no name strings to repeat.
import { defineChannel } from 'use-everywhere';
type ShopEvents = { 'cart-updated': { items: number } };
export const shop = defineChannel<ShopEvents>('shop');import { useState } from 'react';import { shop } from './shop-channel';
function CartBadge() { const [items, setItems] = useState(0); const send = shop.useSend();
shop.useMessage('cart-updated', (p) => setItems(p.items)); // other tabs
const addToCart = () => { setItems(items + 1); // 1. this tab, explicitly send('cart-updated', { items: items + 1 }); // 2. every other tab };
return <button onClick={addToCart}>Cart ({items})</button>;}Compare with the same component built from the trio: the behavior is identical — this version just moved the name and the type parameter out of the component and into the channel’s own module.
Signature
Section titled “Signature”function defineChannel<M extends MessageMap>(name: string): ChannelHooks<M>;Not a hook — a plain factory, meant to run at module scope. It lives in the
React package only (use-everywhere, not @use-everywhere/core), because
what it returns is hooks.
Return value
Section titled “Return value”| Member | Type | What it is |
|---|---|---|
useSend | () => (type, payload) => void | The bound channel’s post, stable identity — same contract as useSend. |
useMessage | (type, handler) => void | Subscribe to one event type — same freshness contract as useMessage. |
get | () => Channel<M> | The underlying channel instance, for code outside React (module-level handlers, workers, tests). |
Everything stays shared
Section titled “Everything stays shared”defineChannel creates no new machinery. It resolves to the same page-wide
channel singleton the standalone hooks use, so all of these are on one wire
and can be mixed freely:
const bound = defineChannel<ShopEvents>('shop');
bound.get() === getChannel('shop'); // same instanceuseChannel<ShopEvents>('shop'); // same instance, in a componentdefineChannel<ShopEvents>('shop').get(); // same instance againTwo modules calling defineChannel('shop') independently talk to each other
— the name is still the identity, exactly like everywhere else in the
library.
Gotchas
Section titled “Gotchas”- Call the returned hooks like hooks.
shop.useSend()andshop.useMessage(...)follow the Rules of Hooks — top level of a component, unconditionally. Thenamespace.useX(...)call shape is fully understood byeslint-plugin-react-hooks. - Don’t rename them during destructuring.
const { useMessage } = shopis fine;const { useMessage: onCart } = shophides the hook from the linter. Calling through the namespace (shop.useMessage) sidesteps the question entirely. - Same semantics as the trio. No echo to the sender, no history,
at-most-once delivery — everything on the
useMessageanduseSendpages applies unchanged.
Where to next
Section titled “Where to next”useChannel/useMessage/useSend— the standalone trio this wraps.- Messages & presence guide — the event system in a full feature.