easy-event-emitter
v1.2.2
Published
Easy event emitter
Maintainers
Readme
Easy Event Emitter
A lightweight, type-safe event emitter implementation for TypeScript and JavaScript applications. This package provides a simple yet powerful way to handle events in your application with support for both isolated and global event handling.
Quick Links to Framework Examples
- React Example
- Vue 3 Example
- Angular Example
- React with Event Stack
- Vue 3 with Event Stack
- Angular with Event Stack
Features
- 🚀 Type-safe event handling with TypeScript
- 🔄 Support for both isolated and global event emitters
- 🎯 Simple and intuitive API
- 📦 Lightweight and zero dependencies
- 🛠️ Built-in event group support
- 🔍 Full TypeScript support with type definitions
- 📚 Stack-based event handling
Installation
npm install easy-event-emitter
# or
yarn add easy-event-emitterQuick Start
import EventEmitter from 'easy-event-emitter';
// Create an isolated event emitter
const events = new EventEmitter();
// Add an event listener
const listener = events.addListener('userLogin', (user) => {
console.log('User logged in:', user);
});
// Emit an event
events.emit('userLogin', { id: 1, name: 'John' });
// Remove the listener when done
listener.remove();API Reference
EventEmitter Class
Constructor
new EventEmitter<T>(isGlobal?: boolean | string)T: Type parameter for event payload types (optional)isGlobal:true: Creates a global event emitterstring: Creates an event emitter in a specific groupfalseorundefined: Creates an isolated event emitter
Instance Methods
addListener
addListener(eventName: string, callback: EventCallback<T>): EventListener<T>Adds an event listener and returns an event object with a remove() method.
eventName: Name of the event to listen forcallback: Function to be called when the event is emitted- Returns:
EventListener<T>object with aremove()method
emit
emit(eventName: string, data: T): voidEmits an event with the specified data.
eventName: Name of the event to emitdata: Data to pass to the event listeners
removeListener
removeListener(): voidRemoves the current event listener.
removeAllListeners
removeAllListeners(): voidRemoves all listeners in the current event emitter instance.
Static Methods
// Static methods with type parameter T
EventEmitter.emit<T>(eventName: string, data: T): void
EventEmitter.addListener<T>(eventName: string, callback: EventCallback<T>): EventListener<T>
EventEmitter.removeListener(handler: Function): void
EventEmitter.removeAllListeners(): voidEvent Class API
The Event class represents a single event listener with its associated handlers and metadata.
Properties
readonly isStack: booleanIndicates whether this event is part of a stack.
get name(): string | undefinedReturns the name of the event.
Methods
emit(data: D): voidEmits the event with the specified data.
hasHandler(handler: EventCallback<D>): booleanChecks if the event has the specified handler.
count(): numberReturns the number of listeners for this event.
remove(): voidRemoves the event listener and cleans up associated resources.
onEmit(handlerEmit: () => void): voidAdds a handler to be called when the event is emitted.
onRemove(handlerRemove: () => void): voidAdds a handler to be called when the event is removed.
Event Stack
The EventEmitter.Stack class provides a way to manage a stack of event listeners. It's useful when you need to handle multiple events in a specific order or maintain a collection of related event listeners.
Constructor
new EventEmitter.Stack<T>(listeners?: Event<T>[])T: Type parameter for event payload types (optional)listeners: Optional array of initial event listeners
Stack Methods
push
push(eventListener: Event<T>): voidAdds an event listener to the stack.
eventListener: The event listener to add to the stack
remove
remove(eventListener?: Event<T>): voidRemoves an event listener from the stack. If no listener is specified, removes the current stack.
eventListener: Optional event listener to remove
length
get length(): numberReturns the number of listeners in the stack (excluding the stack itself).
Stack Example
import EventEmitter from 'easy-event-emitter';
// Create a stack of event listeners
const stack = new EventEmitter.Stack();
// Add listeners to the stack
const listener1 = events.addListener('userAction', (data) => {
console.log('First listener:', data);
});
const listener2 = events.addListener('userAction', (data) => {
console.log('Second listener:', data);
});
// Push listeners to the stack
stack.push(listener1);
stack.push(listener2);
// Emit an event that will be handled by all listeners in the stack
events.emit('userAction', { action: 'click' });
// Remove a specific listener from the stack
stack.remove(listener1);
// Get the number of listeners in the stack
console.log(stack.length); // 1
// Remove the entire stack
stack.remove();Type System
Core Types
// Event callback type
type EventCallback<T> = (data: T) => void;
// Event listener interface
interface EventListener<T = any>Usage Examples
Type-Safe Events
// Define your event types
type UserEvents = {
login: { id: number; name: string };
logout: { id: number };
update: { id: number; data: Partial<User> };
};
// Create a type-safe event emitter
const events = new EventEmitter<UserEvents>();
// TypeScript will provide type checking and autocompletion
events.addListener('login', (user) => {
console.log(`User ${user.name} logged in`);
});
events.emit('login', { id: 1, name: 'John' });Event Groups
// Create event emitters in the same group
const emitter1 = new EventEmitter('userGroup');
const emitter2 = new EventEmitter('userGroup');
// Both emitters will receive events from the group
emitter1.addListener('userUpdate', (data) => {
console.log('Emitter 1 received:', data);
});
emitter2.addListener('userUpdate', (data) => {
console.log('Emitter 2 received:', data);
});
// Emit to the group
emitter1.emit('userUpdate', { id: 1, name: 'John' });Global Events
// Create a global event emitter
const globalEvents = new EventEmitter(true);
// Add a global listener
const listener = globalEvents.addListener('appError', (error) => {
console.error('Global error:', error);
});
// Emit a global event
globalEvents.emit('appError', new Error('Something went wrong'));
// Remove the listener when done
listener.remove();Static Event Handling
// Add a static listener
const listener = EventEmitter.addListener<Error>('appError', (error) => {
console.error('Global error:', error);
});
// Emit a static event
EventEmitter.emit<Error>('appError', new Error('Something went wrong'));
// Remove the listener
EventEmitter.removeListener(listener);Best Practices
- Type Safety: Always define your event types for better type checking and IDE support.
- Memory Management: Remember to remove listeners when they're no longer needed to prevent memory leaks.
- Event Groups: Use event groups when you need to share events between different parts of your application.
- Global Events: Use global events sparingly and only for application-wide concerns.
- Static vs Instance: Use static methods for global events and instance methods for component-specific events.
- Event Stacks: Use stacks when you need to manage multiple related event listeners as a group.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Examples
Isolated
const events1 = new EventEmitter();
const events2 = new EventEmitter();
events1.addListener('test', (val) => {
console.log(val); //hello
});
const listener = events2.addListener('test', (val) => {
console.log(val); //world
});
events2.addListener('test2', (val) => {
console.log(val); //hi :-)
});
events1.emit('test', 'hello');
events2.emit('test', 'world');
events2.emit('test2', 'hi :-)');
listener.remove(); //Remove current listener
events2.removeAllListeners(); //Remove all listeners in current instanceGroups
const events1 = new EventEmitter('test_group');
const events2 = new EventEmitter('test_group');
events1.addListener('test', (val) => {
console.log(val); //"hello" replace "world"
});
events2.addListener('test', (val) => {
console.log(val); //"hello" replace "world"
});
events1.emit('test', 'hello');
events2.emit('test', 'world');Global
const events = new EventEmitter(true);
events.addListener('test', (val) => {
console.log(val); //"hello" replace "world"
});
const listener = EventEmitter.addListener('test', (val) => {
console.log(val); //"hello" replace "world"
});
events.emit('test', 'hello');
EventEmitter.emit('test', 'world');
listener.remove(); //Remove current global listener
events.removeAllListeners(); //Remove all global listeners
//Or
EventEmitter.removeAllListeners();Typing
type TUser = {
id: number;
name: string;
email: string;
};
type TEvents = {
login: TUser;
register: Omit<TUser, 'id'>
};
const events = new EventEmitter<TEvents>();Usage in Modern Frameworks
React Example
// store/eventStore.ts
import EventEmitter from 'easy-event-emitter';
// Create a global event emitter for the application
export const eventEmitter = new EventEmitter();
// Define event types
export interface UserEvent {
id: number;
name: string;
}
// React Component Example
import React, { useEffect } from 'react';
import { eventEmitter, UserEvent } from './store/eventStore';
const UserProfile: React.FC = () => {
useEffect(() => {
// Add event listener when component mounts
const listener = eventEmitter.addListener('userUpdate', (user: UserEvent) => {
console.log('User updated:', user);
// Update component state or trigger re-render
});
// Clean up listener when component unmounts
return () => listener.remove();
}, []);
const handleUpdateUser = () => {
// Emit event when user data is updated
eventEmitter.emit('userUpdate', { id: 1, name: 'John Doe' });
};
return (
<div>
<button onClick={handleUpdateUser}>Update User</button>
</div>
);
};
export default UserProfile;Vue 3 Example
// store/eventStore.ts
import EventEmitter from 'easy-event-emitter';
export const eventEmitter = new EventEmitter();
// Vue Component Example
import { defineComponent, onMounted, onUnmounted } from 'vue';
import { eventEmitter } from './store/eventStore';
export default defineComponent({
setup() {
let listener: any;
onMounted(() => {
// Add event listener when component is mounted
listener = eventEmitter.addListener('dataUpdate', (data) => {
console.log('Data updated:', data);
// Update component state
});
});
onUnmounted(() => {
// Clean up listener when component is unmounted
if (listener) {
listener.remove();
}
});
const updateData = () => {
eventEmitter.emit('dataUpdate', { value: 'new data' });
};
return {
updateData
};
}
});Angular Example
// services/event.service.ts
import { Injectable } from '@angular/core';
import EventEmitter from 'easy-event-emitter';
@Injectable({
providedIn: 'root'
})
export class EventService {
private eventEmitter = new EventEmitter();
emitEvent(eventName: string, data: any) {
this.eventEmitter.emit(eventName, data);
}
addListener(eventName: string, callback: (data: any) => void) {
return this.eventEmitter.addListener(eventName, callback);
}
}
// component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { EventService } from './services/event.service';
@Component({
selector: 'app-example',
template: `
<button (click)="updateData()">Update Data</button>
`
})
export class ExampleComponent implements OnInit, OnDestroy {
private listener: any;
constructor(private eventService: EventService) {}
ngOnInit() {
// Add event listener when component initializes
this.listener = this.eventService.addListener('dataUpdate', (data) => {
console.log('Data updated:', data);
// Update component state
});
}
ngOnDestroy() {
// Clean up listener when component is destroyed
if (this.listener) {
this.listener.remove();
}
}
updateData() {
this.eventService.emitEvent('dataUpdate', { value: 'new data' });
}
}These examples demonstrate how to integrate the EventEmitter with modern frameworks while following their best practices and lifecycle management patterns. The EventEmitter can be used to create a centralized event system in your application, making it easy to communicate between different components and services.
Using EventEmitter.Stack in Modern Frameworks
React Example with Event Stack
// store/eventStore.ts
import EventEmitter from 'easy-event-emitter';
export const eventEmitter = new EventEmitter();
// Define event types
export interface UserEvents {
profileUpdate: { id: number; name: string };
settingsUpdate: { theme: string; notifications: boolean };
statusUpdate: { online: boolean };
}
// React Component Example with Stack
import React, { useEffect } from 'react';
import { eventEmitter, UserEvents } from './store/eventStore';
const UserDashboard: React.FC = () => {
useEffect(() => {
// Create stack inside useEffect
const stack = new EventEmitter.Stack();
// Create and add listeners to the stack
stack.push(
eventEmitter.addListener('profileUpdate', (data) => {
console.log('Profile updated:', data);
})
);
stack.push(
eventEmitter.addListener('settingsUpdate', (data) => {
console.log('Settings updated:', data);
})
);
stack.push(
eventEmitter.addListener('statusUpdate', (data) => {
console.log('Status updated:', data);
})
);
// Clean up stack when component unmounts
return () => stack.remove();
}, []);
const handleUpdates = () => {
// Emit multiple events
eventEmitter.emit('profileUpdate', { id: 1, name: 'John' });
eventEmitter.emit('settingsUpdate', { theme: 'dark', notifications: true });
eventEmitter.emit('statusUpdate', { online: true });
};
return (
<div>
<button onClick={handleUpdates}>Update All</button>
</div>
);
};
export default UserDashboard;Vue 3 Example with Event Stack
// store/eventStore.ts
import EventEmitter from 'easy-event-emitter';
export const eventEmitter = new EventEmitter();
// Vue Component Example with Stack
import { defineComponent, onMounted, onUnmounted } from 'vue';
import { eventEmitter } from './store/eventStore';
export default defineComponent({
setup() {
let stack: EventEmitter.Stack;
onMounted(() => {
// Create stack inside onMounted
stack = new EventEmitter.Stack();
// Add listeners to the stack
stack.push(
eventEmitter.addListener('dataUpdate', (data) => {
console.log('Data updated:', data);
})
);
stack.push(
eventEmitter.addListener('configUpdate', (config) => {
console.log('Config updated:', config);
})
);
stack.push(
eventEmitter.addListener('stateUpdate', (state) => {
console.log('State updated:', state);
})
);
});
onUnmounted(() => {
// Clean up stack when component is unmounted
if (stack) {
stack.remove();
}
});
const updateAll = () => {
// Emit multiple events
eventEmitter.emit('dataUpdate', { value: 'new data' });
eventEmitter.emit('configUpdate', { setting: 'new setting' });
eventEmitter.emit('stateUpdate', { status: 'active' });
};
return {
updateAll
};
}
});Angular Example with Event Stack
// services/event.service.ts
import { Injectable } from '@angular/core';
import EventEmitter from 'easy-event-emitter';
@Injectable({
providedIn: 'root'
})
export class EventService {
private eventEmitter = new EventEmitter();
emitEvent(eventName: string, data: any) {
this.eventEmitter.emit(eventName, data);
}
addListener(eventName: string, callback: (data: any) => void) {
return this.eventEmitter.addListener(eventName, callback);
}
}
// component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { EventService } from './services/event.service';
@Component({
selector: 'app-dashboard',
template: `
<button (click)="updateAll()">Update All</button>
`
})
export class DashboardComponent implements OnInit, OnDestroy {
private stack: EventEmitter.Stack;
constructor(private eventService: EventService) {}
ngOnInit() {
// Create stack inside ngOnInit
this.stack = new EventEmitter.Stack();
// Add listeners to the stack
this.stack.push(
this.eventService.addListener('userUpdate', (data) => {
console.log('User updated:', data);
})
);
this.stack.push(
this.eventService.addListener('preferencesUpdate', (data) => {
console.log('Preferences updated:', data);
})
);
this.stack.push(
this.eventService.addListener('activityUpdate', (data) => {
console.log('Activity updated:', data);
})
);
}
ngOnDestroy() {
// Clean up stack when component is destroyed
if (this.stack) {
this.stack.remove();
}
}
updateAll() {
// Emit multiple events
this.eventService.emitEvent('userUpdate', { id: 1, name: 'John' });
this.eventService.emitEvent('preferencesUpdate', { theme: 'dark' });
this.eventService.emitEvent('activityUpdate', { status: 'active' });
}
}These examples demonstrate the correct usage of EventEmitter.Stack in modern frameworks, where:
- The stack is created within the component's lifecycle (useEffect, onMounted, ngOnInit)
- Event listeners are added to the stack immediately after creation
- The stack is cleaned up when the component is destroyed
- The stack is scoped to the component instance
This approach ensures that:
- Event listeners are properly managed within the component's lifecycle
- Memory leaks are prevented by cleaning up the stack
- The stack is isolated to the specific component instance
- Event handling is more organized and maintainable
