A powerful and lightweight global event system for React and React Native applications. Solve complex component communication issues, eliminate prop drilling, and enable seamless cross-component function calls with ease.
- Universal Compatibility: Works with both React and React Native
- Dual API: Class-based and Hook-based APIs for different use cases
- TypeScript Support: Full TypeScript support with comprehensive type definitions
- Priority System: Execute listeners in priority order
- Scoped Events: Organize events with namespaces
- Automatic Cleanup: Hooks automatically clean up listeners on unmount
- Error Handling: Graceful error handling with optional warnings
- Performance Optimized: Lightweight and efficient event management
- Comprehensive Testing: 100% test coverage with Jest
npm install react-global-event
yarn add react-global-event
This library solves common React/React Native communication challenges:
- Prop Drilling: Eliminate the need to pass callbacks through multiple component levels
- Child-to-Parent Communication: Allow deeply nested components to communicate with parents
- Cross-Component Function Calls: Call functions in any component from anywhere in your app
- Redux/Saga Integration: Trigger component functions from Redux actions or Saga effects
- Module Communication: Enable communication between different modules or features
import { globalEvent } from 'react-global-event';
// Emit an event
globalEvent.emit('user-login', { userId: 123, username: 'john' });
// Listen to an event
const subscription = globalEvent.on('user-login', (userData) => {
console.log('User logged in:', userData);
});
// Clean up
subscription.remove();
import React from 'react';
import { useGlobalEvent, useGlobalEventListener } from 'react-global-event';
function MyComponent() {
const { emit } = useGlobalEvent();
useGlobalEventListener('data-loaded', (data) => {
console.log('Data received:', data);
});
const handleClick = () => {
emit('button-clicked', { timestamp: Date.now() });
};
return <button onClick={handleClick}>Click Me</button>;
}
The main class for managing global events.
import { GlobalEvent } from 'react-global-event';
const globalEvent = new GlobalEvent();
Methods:
emit<T>(eventName: string, data?: T): void
- Emit an eventon<T>(eventName: string, listener: EventListener<T>, options?: { once?: boolean; priority?: number }): EventSubscription
- Register a listeneroff<T>(eventName: string, listener?: EventListener<T> | string): void
- Remove a listeneronce<T>(eventName: string, listener: EventListener<T>): EventSubscription
- Register a one-time listenerremoveAllListeners(eventName?: string): void
- Remove all listenersgetListenerCount(eventName: string): number
- Get listener countgetEventNames(): string[]
- Get all event namesscope(namespace: string): ScopedGlobalEvent
- Create a scoped event managerbatchEmit(events: Array<{ name: string; data?: any }>): void
- Emit multiple events
Namespace-aware event manager for better organization.
const userEvents = globalEvent.scope('user');
userEvents.emit('login', userData); // Actually emits 'user:login'
Main hook for event management with automatic cleanup.
const { emit, on, off, once, removeAllListeners, getListenerCount, getEventNames } = useGlobalEvent();
Hook for listening to a specific event with automatic cleanup.
useGlobalEventListener('user-login', (userData) => {
console.log('User logged in:', userData);
}, [dependency]);
Hook for one-time event listening.
useGlobalEventOnce('initial-data-loaded', (data) => {
console.log('Initial data loaded:', data);
});
Hook for scoped event management.
const userEvents = useScopedGlobalEvent('user');
userEvents.emit('login', userData);
Hook for managing component state based on events.
const [userData, setUserData] = useGlobalEventState('user-updated', null);
const [isOnline, setIsOnline] = useGlobalEventState(
'connection-status',
false,
(data) => data.status === 'connected'
);
Problem: Passing callbacks through multiple component levels
// β Before: Prop drilling
function App() {
const handleUserAction = (action) => {
console.log('User action:', action);
};
return <Parent onUserAction={handleUserAction} />;
}
function Parent({ onUserAction }) {
return <Child onUserAction={onUserAction} />;
}
function Child({ onUserAction }) {
return <DeepChild onUserAction={onUserAction} />;
}
function DeepChild({ onUserAction }) {
return <button onClick={() => onUserAction('clicked')}>Click</button>;
}
// β
After: Global events
function App() {
useGlobalEventListener('user-action', (action) => {
console.log('User action:', action);
});
return <Parent />;
}
function DeepChild() {
const { emit } = useGlobalEvent();
return (
<button onClick={() => emit('user-action', 'clicked')}>
Click
</button>
);
}
Problem: Calling functions in one component from another
// Component A - Has data that others need
function ComponentA() {
const [data, setData] = useState('Initial data');
useGlobalEventListener('get-data', () => {
globalEvent.emit('data-response', { data });
});
useGlobalEventListener('update-data', (newData) => {
setData(newData);
});
return <div>Data: {data}</div>;
}
// Component B - Needs data from Component A
function ComponentB() {
const [receivedData, setReceivedData] = useState('');
useGlobalEventListener('data-response', (response) => {
setReceivedData(response.data);
});
const getData = () => {
globalEvent.emit('get-data');
};
const updateData = () => {
globalEvent.emit('update-data', 'Updated by Component B');
};
return (
<div>
<p>Data from A: {receivedData}</p>
<button onClick={getData}>Get Data</button>
<button onClick={updateData}>Update Data</button>
</div>
);
}
Problem: Triggering component functions from Redux actions or Saga effects
// Redux Action
const loginUser = (userData) => {
return {
type: 'LOGIN_USER',
payload: userData
};
};
// Saga Effect
function* loginUserSaga(action) {
try {
const userData = yield call(api.login, action.payload);
yield put({ type: 'LOGIN_SUCCESS', payload: userData });
// Trigger component function via global event
globalEvent.emit('redux-user-login', userData);
} catch (error) {
globalEvent.emit('redux-login-error', error);
}
}
// Component that reacts to Redux events
function UserProfile() {
const [user, setUser] = useState(null);
useGlobalEventListener('redux-user-login', (userData) => {
setUser(userData);
});
useGlobalEventListener('redux-login-error', (error) => {
console.error('Login failed:', error);
});
return user ? <div>Welcome, {user.name}!</div> : <div>Please login</div>;
}
Problem: Communication between different modules or features
// User Module
function UserModule() {
const userEvents = useScopedGlobalEvent('user');
const updateProfile = (profileData) => {
// Update profile logic
userEvents.emit('profile-updated', profileData);
};
return <button onClick={() => updateProfile({ name: 'John' })}>Update Profile</button>;
}
// Notification Module
function NotificationModule() {
useGlobalEventListener('user:profile-updated', (profileData) => {
showNotification(`Profile updated: ${profileData.name}`);
});
return <div>Notifications</div>;
}
Problem: Keeping component state in sync with global events
function ConnectionStatus() {
const [isOnline, setIsOnline] = useGlobalEventState(
'connection-status',
false,
(data) => data.status === 'connected'
);
const [userCount, setUserCount] = useGlobalEventState('user-count', 0);
return (
<div>
<p>Status: {isOnline ? 'Online' : 'Offline'}</p>
<p>Users: {userCount}</p>
</div>
);
}
import { EventManager } from 'react-global-event';
const eventManager = EventManager.getInstance({
maxListeners: 50, // Maximum listeners per event
enableWarnings: true // Enable console warnings
});
// Higher priority listeners execute first
globalEvent.on('data-loaded', highPriorityHandler, { priority: 10 });
globalEvent.on('data-loaded', normalHandler, { priority: 5 });
globalEvent.on('data-loaded', lowPriorityHandler, { priority: 1 });
The library includes comprehensive unit tests. Run tests with:
npm test
npm run test:coverage
- Lightweight: Minimal bundle size impact
- Efficient: Optimized event handling and cleanup
- Memory Safe: Automatic cleanup prevents memory leaks
- Type Safe: Full TypeScript support for better development experience
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by the need to solve complex component communication in React applications
- Built with TypeScript for better developer experience
- Comprehensive testing ensures reliability
Made with β€οΈ for the React community