npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@protoworx/react-ripple-effect

v0.0.6

Published

A package for the react ripple effect

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-effect

Quick 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:

  • EventDriver is an in‑memory event bus.
  • EventProvider makes it available to your React tree via context.
  • useTriggerEvent returns a function you can call with an event key and payload.
  • useMonitorEvent subscribes 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 key to known event names in your AppEvents map.
  • 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 debounce and throttle are 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).
  • trigger(key: string, ...args: any[]): void
    • Calls all listeners registered for key with the given arguments.
  • getListenerCount(key: string): number
    • Returns the number of active listeners for key.
  • hasListeners(key: string): boolean
    • Returns true if there is at least one listener for key.
  • 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 EventDriver per application (or per logical scope) and share it via EventProvider.
  • 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/catch and logs errors to console.error so one failing listener doesn’t break others.

License

MIT