Skip to content

Instantly share code, notes, and snippets.

@denisputnov
Created September 12, 2024 12:48
Show Gist options
  • Save denisputnov/8e53d557a513ffb0ecb45e6713a18e5e to your computer and use it in GitHub Desktop.
Save denisputnov/8e53d557a513ffb0ecb45e6713a18e5e to your computer and use it in GitHub Desktop.
import {useEffect} from 'react';
type EventKey = string;
type EventHandler<T = any> = (payload: T) => void;
type EventMap = Record<EventKey, EventHandler>;
type Bus<E> = Record<keyof E, E[keyof E][]>;
interface EventBus<T extends EventMap> {
on<Key extends keyof T>(key: Key, handler: T[Key]): () => void;
off<Key extends keyof T>(key: Key, handler: T[Key]): void;
once<Key extends keyof T>(key: Key, handler: T[Key]): void;
emit<Key extends keyof T>(key: Key, ...payload: Parameters<T[Key]>): void;
useEvent<Key extends keyof T>(key: Key, handler: T[Key]): void;
}
interface EventBusConfig {
onError: (...params: any[]) => void;
}
export const createEventChannel = <E extends EventMap>(config?: EventBusConfig): EventBus<E> => {
const bus: Partial<Bus<E>> = {};
const on: EventBus<E>['on'] = (key, handler) => {
if (bus[key] === undefined) {
bus[key] = [];
}
bus[key]?.push(handler);
return () => {
off(key, handler);
};
};
const off: EventBus<E>['off'] = (key, handler) => {
const index = bus[key]?.indexOf(handler) ?? -1;
bus[key]?.splice(index >>> 0, 1);
};
const once: EventBus<E>['once'] = (key, handler) => {
const handleOnce = (payload: Parameters<typeof handler>) => {
handler(payload);
off(key, handleOnce as typeof handler);
};
on(key, handleOnce as typeof handler);
};
const emit: EventBus<E>['emit'] = (key, payload) => {
bus[key]?.forEach(fn => {
try {
fn(payload);
} catch (e) {
config?.onError(e);
}
});
};
const useEvent: EventBus<E>['useEvent'] = (key, handler) => {
useEffect(() => {
return on(key, handler);
}, []);
};
return {on, off, once, emit, useEvent};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment