@protoworx/react-ripple-effect
v0.0.6
Published
A package for the react ripple effect
Maintainers
Readme
React Ripple Effect
A tiny, hook-based event bus for React that lets your components communicate via named events.
@protoworx/react-ripple-effect gives you:
- A lightweight in‑memory event driver.
- A React provider to expose it to your component tree.
- Hooks to trigger and monitor events, with optional debounce/throttle.
- Optional TypeScript type safety for event keys and payloads.
Installation
npm install @protoworx/react-ripple-effect
# or
yarn add @protoworx/react-ripple-effect
# or
pnpm add @protoworx/react-ripple-effectQuick start
Create a single EventDriver, wrap your app with EventProvider, then use hooks to trigger and monitor events:
'use client';
import {
EventDriver,
EventProvider,
useMonitorEvent,
useTriggerEvent,
} from '@protoworx/react-ripple-effect';
const client = new EventDriver();
export default function App() {
return (
<EventProvider client={client}>
<TriggerEventButton />
<MonitorEventLog />
</EventProvider>
);
}
function TriggerEventButton() {
const trigger = useTriggerEvent();
return (
<button onClick={() => trigger('test', 'Hello, world!')}>
Trigger Event
</button>
);
}
function MonitorEventLog() {
useMonitorEvent({
test: (data: string) => {
console.log('Test event:', data);
},
});
return <div>Listening for "test" events…</div>;
}How it works:
EventDriveris an in‑memory event bus.EventProvidermakes it available to your React tree via context.useTriggerEventreturns a function you can call with an event key and payload.useMonitorEventsubscribes to one or more event keys and runs your callbacks whenever those events fire.
Type‑safe events (TypeScript)
You can define an EventMap to get full type safety for event keys and payloads:
type AppEvents = {
'user-logged-in': { userId: number; username: string };
'toast.show': { message: string; type?: 'info' | 'error' };
};Use it with the hooks:
// Triggering
const trigger = useTriggerEvent<AppEvents>();
trigger('user-logged-in', { userId: 1, username: 'alice' });
trigger('toast.show', { message: 'Saved!', type: 'info' });
// Monitoring
useMonitorEvent<AppEvents>({
'user-logged-in': (data) => {
// data: { userId: number; username: string }
console.log('Logged in as', data.username);
},
'toast.show': (data) => {
console.log('Toast:', data.message);
},
});TypeScript will:
- Restrict
keyto known event names in yourAppEventsmap. - Enforce the correct payload type per key.
Debounce & throttle with useMonitorEvent
useMonitorEvent accepts either simple callbacks or configuration objects that support debounce and throttle:
useMonitorEvent({
'search:input': {
callback: (query: string) => {
// Only fire after the user stops typing for 300ms
performSearch(query);
},
debounce: 300,
},
'window:scroll': {
callback: () => {
// At most once per 100ms
updateScrollPosition();
},
throttle: 100,
},
});Notes:
- If both
debounceandthrottleare specified, debounce wins and a warning is logged. - All subscriptions and timers are cleaned up automatically when the component unmounts.
API reference
EventDriver
A minimal in‑memory event bus.
import { EventDriver } from '@protoworx/react-ripple-effect';
const driver = new EventDriver();Methods:
subscribe(key: string, callback: (...args: any[]) => void): () => void- Registers a listener for
key. - Returns an unsubscribe function (idempotent).
- Registers a listener for
trigger(key: string, ...args: any[]): void- Calls all listeners registered for
keywith the given arguments.
- Calls all listeners registered for
getListenerCount(key: string): number- Returns the number of active listeners for
key.
- Returns the number of active listeners for
hasListeners(key: string): boolean- Returns
trueif there is at least one listener forkey.
- Returns
getEventKeys(): string[]- Returns all event keys that currently have listeners.
cleanup(): void- Removes all listeners for all keys.
<EventProvider />
import { EventProvider, EventDriver } from '@protoworx/react-ripple-effect';
const client = new EventDriver();
export function Root({ children }: { children: React.ReactNode }) {
return <EventProvider client={client}>{children}</EventProvider>;
}Props:
client: EventDriver– the event driver instance to use.maxListeners?: number– maximum number of listeners per key (reserved for future limits; currently not enforced).debug?: boolean– flag to enable additional logging (reserved for future use).
useTriggerEvent
const trigger = useTriggerEvent(); // or useTriggerEvent<AppEvents>()
trigger('some-event', { foo: 'bar' });- Returns a function to trigger events by key.
- When used with a generic
EventMap, keys and payloads are fully typed.
useMonitorEvent
useMonitorEvent({
'some-event': (payload) => {
console.log('Got some-event with', payload);
},
});- Accepts an object mapping event keys to:
- A callback function, or
- A config object
{ callback, debounce?, throttle? }.
- Automatically subscribes on mount and unsubscribes on unmount.
Best practices
- Create a single
EventDriverper application (or per logical scope) and share it viaEventProvider. - Use descriptive event keys like
'user:logged-in','cart:item-added','toast.show'. - Prefer events for cross-cutting concerns (analytics, toasts, background sync) where components shouldn’t know about each other directly.
- Use type-safe
EventMaps in TypeScript code for better DX and safer refactoring.
Error handling & validation
Internally, the event driver:
- Validates that event keys are non‑empty strings and callbacks are functions, throwing helpful errors otherwise.
- Wraps each listener call in a
try/catchand logs errors toconsole.errorso one failing listener doesn’t break others.
License
MIT
