UNPKG

@equinor/fusion-react-utils

Version:
72 lines 3.51 kB
import { useLayoutEffect, useMemo, useRef } from 'react'; import { createSyntheticEvent } from '../create-synthetic-event'; import { shallowEqual } from '../shallow-equal'; const noEvents = {}; /** * Add event listeners to a native element * @param ref React ref object of native element * @param eventHandlers handler to attach to element, must match map (normally props of component) * @param eventMap Map of events where key is prop name and value is event name */ export const useElementEvents = (ref, eventHandlers, eventMap = {}) => { /** keep a reference of all registered events */ const listenersRef = useRef({}); /** keep a reference of all registered event handler */ const handlersRef = useRef({}); /** memorize provided event handlers provided by component properties */ const handlers = useMemo(() => { /** early exit, no props provided */ if (!eventHandlers) return noEvents; /** extract properties that are defined as custom event handlers */ const handlers = Object.keys(eventMap) .filter((k) => k in eventHandlers) .reduce((cur, key) => Object.assign(cur, { [key]: eventHandlers[key] }), {}); /** compare event handlers with existing */ const hasChanged = !shallowEqual(handlersRef.current, handlers); /** only return if event handlers has changes */ return hasChanged ? handlers : handlersRef.current; }, [handlersRef, eventMap, eventHandlers]); /** * Check for changes in event handlers as an side effect * No teardown since registered event listeners create synthetic react events that access * the referenced callback. We also assume that when this component, the event target is also * unmounted and do not need teardown. */ useLayoutEffect(() => { if (!handlers) return; const listeners = listenersRef.current; const propKeys = Object.keys(eventMap).filter((k) => k in handlers); /** register all new events */ for (const propKey of propKeys) { if (!(propKey in listeners)) { /** internal callback that generate synthetic event that reference provided callback */ const eventListener = (e) => { const handler = handlersRef.current[propKey]; handler && handler(createSyntheticEvent(e)); }; console.debug(`adding event listener [${propKey}]`, ref.current); /** register listener and add to index */ ref.current?.addEventListener(eventMap[propKey], eventListener); Object.assign(listeners, { [propKey]: eventListener }); } } /** generate list over events removed in props */ const removed = Object.keys(listeners).filter((k) => !(k in handlers)); for (const key of removed) { console.debug(`removing event listener [${key}]`, ref.current); const type = eventMap[key]; const handler = listeners[key]; if (type) { handler && ref.current?.removeEventListener(type, handler); delete listeners[key]; } } /** assign indexes to reference */ listenersRef.current = listeners; handlersRef.current = handlers; }, [eventMap, ref, handlersRef, listenersRef, handlers]); }; export default useElementEvents; //# sourceMappingURL=useElementEvents.js.map