UNPKG

@deathbyjer/react-event-manager

Version:

Handle custom events in react and enable components to broadcast and consume dynamic messaging

174 lines (130 loc) 5.21 kB
import React, { useEffect, useContext } from "react" class EventManager { /* * Event Conditions ********************/ addEventCondition(event_type, event_condition) { if (typeof event_condition != "function") return this.__getEvents(event_type).conditions.push(event_condition) } removeEventCondition(event_type, event_condition) { const index = this.__getEvents(event_type).conditions.indexOf(event_condition) if (index >= 0) this.__getEvents(event_type).conditions.splice(index, 1) } /* * Event Listeners **********************/ addEventListener(event_type, event_listener) { if (typeof event_listener != "function") return this.__getEvents(event_type).listeners.push(event_listener) } removeEventListener(event_type, event_listener) { const index = this.__getEvents(event_type).listeners.indexOf(event_listener) if (index >= 0) this.__getEvents(event_type).listeners.splice(index, 1) } /* * Call the event listener ***************************/ applyEventListeners(event_type, event_data) { for (let event_condition of this.__getEvents(event_type).conditions) if (!event_condition(event_data)) return for (let event_listener of this.__getEvents(event_type).listeners) event_listener(event_data) } removeAllEventListeners(event_type) { const event = __getEvents(event_type) event.conditions.length = 0 event.listeners.length = 0 delete this.events[event_type] } __getEvents(event_type) { if (!this.events) this.events = {} if (!this.events[event_type]) this.events[event_type] = {conditions: [], listeners: []} return this.events[event_type] } } const EventContext = React.createContext(new EventManager()) export function Provider(props) { const events = new EventManager() return React.createElement(EventContext.Provider, { value: events}, props.children) } export function connectToEventManager(Component) { return function(props) { const children = events => React.createElement(Component, Object.assign({ events: events }, props)) return React.createElement(EventContext.Consumer, null, children) } } const METHOD_TYPES = ["conditions", "listeners"] function ensureBoundObject(listeners) { // It can only be a function, an object or an array. Otherwise, it's wrong if (!["function", "object"].includes(typeof listeners)) return { conditions: [], listeners: [] } // If it's a function, we're going to make it an array if (typeof listeners == "function") listeners = [ listeners ] // Now we're going to handle it not being a non-array object. // The onlt other thing it could be is an array. So in that case // we'll turn it into an object if (Array.isArray(listeners)) listeners = { listeners } // Now we are going to ensure the event object properly const event = listeners for (let attr of METHOD_TYPES) { // If the attribute is a function, make it an array if (typeof event[attr] == "function") event[attr] = [event[attr]] // If the attribute is not an array, then I don't care about it if (!Array.isArray(event[attr])) event[attr] = [] // Ensure every element inside the array is a function event[attr] = event[attr].filter(method => typeof method == "function") } return event } export function bindListeners(event_manager, context, listeners) { const bound_listeners = { manager: event_manager, events: {} } const events = bound_listeners.events for (let event_type in listeners) { let event = ensureBoundObject(listeners[event_type]) events[event_type] = {} for (let method_type of METHOD_TYPES) events[event_type][method_type] = event[method_type].map(method => method.bind(context)) for (let method of events[event_type].conditions) event_manager.addEventCondition(event_type, method) for (let method of events[event_type].listeners) event_manager.addEventListener(event_type, method) } return bound_listeners } export function removeBoundListeners(bound_listeners) { const event_manager = bound_listeners.manager const events = bound_listeners.events for (let event_type in events) { for (let event_condition of events[event_type].conditions) event_manager.removeEventCondition(event_type, event_condition) events[event_type].conditions = 0 for (let event_listener of events[event_type].listeners) event_manager.removeEventListener(event_type, event_listener) events[event_type].listeners = 0 delete events[event_type] } delete bound_listeners.events delete bound_listeners.manager } export function useEventManager() { return useContext(EventContext) } export function useEventListeners(listeners, { context } = {}) { const events = useContext(EventContext) useEffect(() => { const boundListeners = bindListeners(events, context, listeners) return () => removeBoundListeners(boundListeners) }) }