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

react-eve-hook

v1.0.12

Published

React hook for component-scoped event management with automatic cleanup using mitt

Readme

React Eve Hook

CI npm npm downloads License

🎯 Component-scoped event management with automatic cleanup for React

A lightweight React hook that provides clean event handling with automatic cleanup when components unmount. Built on top of mitt with full TypeScript support.

✨ Features

  • 🧹 Automatic cleanup on component unmount
  • 🎭 Anonymous function support - no more handler reference management
  • 🔧 Flexible removal options - remove all, by event, or specific handlers
  • 🚫 Duplicate prevention - prevents accidental double registration
  • 📘 Full TypeScript support with type-safe events
  • Lightweight - only 200 bytes (mitt) + minimal wrapper
  • ⚛️ React-friendly - designed for the hooks pattern

📦 Installation

npm install react-eve-hook
# or
yarn add react-eve-hook
# or
pnpm add react-eve-hook

🚀 Quick Start

import useEve, { eve } from 'react-eve-hook';

function MyComponent() {
  const evn = useEve();

  useEffect(() => {
    // Register events with anonymous functions (no cleanup needed!)
    evn.on('user-login', (user) => console.log('User logged in:', user));
    evn.on('notification', (msg) => showToast(msg));
    evn.on('data-update', (data) => updateUI(data));
    
    // return () => evn.off(); // No cleanup is required—all events are automatically cleaned up on unmount
  }, []);

  const handleClick = () => {
    evn.emit('user-login', { name: 'Alice', id: 123 });
  };

  return <button onClick={handleClick}>Login</button>;
}

// Emit events from anywhere in your app
function OtherComponent() {
  const handleSomething = () => {
    eve('notification', { message: 'Hello!', type: 'success' });
  };
}

🎯 Type-Safe Events

Define your event types for full type safety:

interface AppEvents {
  'user-login': { name: string; id: number };
  'user-logout': undefined;
  'notification': { message: string; type: 'info' | 'error' | 'success' };
}

function TypeSafeComponent() {
  const evn = useEve<AppEvents>();

  useEffect(() => {
    evn.on('user-login', (user) => {
      // user is automatically typed as { name: string; id: number }
      console.log(user.name, user.id);
    });

    evn.on('notification', (notif) => {
      // notif is typed as { message: string; type: 'info' | 'error' | 'success' }
      if (notif.type === 'error') {
        // Type narrowing works!
      }
    });
  }, []);

  const handleLogin = () => {
    evn.emit('user-login', { name: 'Bob', id: 456 }); // ✅ Type checked
    // evn.emit('user-login', { invalid: true }); // ❌ TypeScript error
  };
}

📚 API Reference

useEve<T>()

Returns an event management object with the following methods:

evn.on(event, handler)

Register an event listener.

evn.on('event-name', (data) => console.log(data));

evn.off(event?, handler?)

Remove event listeners with flexible options:

evn.off()                          // Remove all events from this component
evn.off('user-login')              // Remove all 'user-login' handlers
evn.off('user-login', handleLogin) // Remove specific handler
evn.off(null, handleLogin)         // Remove handler from all events

evn.clear()

Remove all event listeners registered by this component (alias for evn.off()).

evn.emit(event, data?)

Emit an event with optional data.

evn.emit('user-login', { name: 'Alice' });
evn.emit('simple-event'); // No data

useEveListen<T>(event, handler)

Simple hook for direct event listening (manual cleanup not required):

useEveListen('user-login', (user) => {
  console.log('User:', user);
});

eve<T>(event, data?)

Global function to emit events from anywhere:

import { eve } from 'react-eve-hook';

eve('notification', { message: 'Global event!' });

🔄 Migration from DOM Events

Before (DOM events):

useEffect(() => {
  const handler1 = (e) => console.log(e);
  const handler2 = (e) => console.log(e);
  
  element.addEventListener('click', handler1);
  element.addEventListener('scroll', handler2);
  
  return () => {
    element.removeEventListener('click', handler1);
    element.removeEventListener('scroll', handler2);
  };
}, []);

After (react-eve-hook):

const evn = useEve();

useEffect(() => {
  evn.on('click', (e) => console.log(e));
  evn.on('scroll', (e) => console.log(e));

  // return () => evn.off();
  // Cleanup is optional - listeners are automatically cleared when the component unmounts
}, []);

💡 Pro Tips

No Manual Cleanup Needed (Most Cases)

When using useEffect with empty dependencies, manual cleanup is often unnecessary thanks to automatic cleanup on unmount:

function MyComponent() {
  const evn = useEve();

  useEffect(() => {
    // These events will be automatically cleaned up when component unmounts
    evn.on('user-login', (user) => console.log('User logged in:', user));
    evn.on('notification', (msg) => showToast(msg));
    evn.on('data-update', (data) => updateUI(data));
    
    // ✅ No cleanup needed! Automatic on unmount
    // return () => evn.off(); // Optional, but not required
  }, []); // Empty deps = mount once, cleanup on unmount

  return <div>My Component</div>;
}

When Manual Cleanup IS Needed

Manual cleanup is useful when you want to remove listeners before component unmounts:

function ConditionalListeners() {
  const evn = useEve();
  const [isListening, setIsListening] = useState(false);

  useEffect(() => {
    if (isListening) {
      evn.on('some-event', handleEvent);
      
      // Clean up when isListening changes to false
      return () => evn.off('some-event', handleEvent);
    }
  }, [isListening]); // Dependencies change = cleanup needed

  return (
    <button onClick={() => setIsListening(!isListening)}>
      {isListening ? 'Stop' : 'Start'} Listening
    </button>
  );
}

Best Practices Summary

  1. Empty deps + anonymous functions = No manual cleanup needed

    useEffect(() => {
      evn.on('event', () => { /* anonymous function */ });
      // Automatic cleanup on unmount ✅
    }, []);
  2. Changing deps = Manual cleanup recommended

    useEffect(() => {
      evn.on('event', () => { /* uses changing state */ });
      return () => evn.off('event'); // Manual cleanup ✅
    }, [changingDependency]);
  3. Conditional listeners = Always use manual cleanup

    useEffect(() => {
      if (condition) {
        evn.on('event', handler);
        return () => evn.off('event', handler); // Required ✅
      }
    }, [condition]);

🤔 Why This Hook?

This hook solves common React event handling pain points:

  1. Manual cleanup complexity - No more managing handler references
  2. Anonymous function support - Write cleaner, more readable code
  3. Component-scoped events - Events are automatically tied to component lifecycle
  4. Type safety - Full TypeScript support with custom event types

🙏 Acknowledgments

This project was developed with assistance from Claude AI for TypeScript refinements, documentation, build configuration, and rubber duck debugging sessions. The core concept and implementation were human-driven!

📄 License

MIT © Naoto Sera

🤝 Contributing

Contributions welcome! Please read our contributing guide first.

🐛 Issues

Found a bug? Please open an issue.