From 6a8dd23d29624dc6ef7f2fe219f1af0cd9eaedf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 29 Jul 2022 11:32:03 +0200 Subject: [PATCH] Add support for `EventTarget` to `useEventEmitter()` (#9060) --- src/hooks/useEventEmitter.ts | 58 +++++++++++++++++++++++++++++++++-- test/test-utils/test-utils.ts | 1 + 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/hooks/useEventEmitter.ts b/src/hooks/useEventEmitter.ts index fccc313b42..3ae011f4ab 100644 --- a/src/hooks/useEventEmitter.ts +++ b/src/hooks/useEventEmitter.ts @@ -32,7 +32,9 @@ export function useTypedEventEmitter< useEventEmitter(emitter, eventName, handler); } -// Hook to wrap event emitter on and removeListener in hook lifecycle +/** + * Hook to wrap an EventEmitter on and off in hook lifecycle + */ export function useEventEmitter( emitter: EventEmitter | undefined, eventName: string | symbol, @@ -59,7 +61,44 @@ export function useEventEmitter( // Remove event listener on cleanup return () => { - emitter.removeListener(eventName, eventListener); + emitter.off(eventName, eventListener); + }; + }, + [eventName, emitter], // Re-run if eventName or emitter changes + ); +} + +/** + * Hook to wrap an EventTarget addEventListener and removeEventListener in hook + * lifecycle + */ +export function useEventTarget( + emitter: EventTarget | undefined, + eventName: string, + handler: Handler, +): void { + // Create a ref that stores handler + const savedHandler = useRef(handler); + + // Update ref.current value if handler changes. + useEffect(() => { + savedHandler.current = handler; + }, [handler]); + + useEffect( + () => { + // allow disabling this hook by passing a falsy emitter + if (!emitter) return; + + // Create event listener that calls handler function stored in ref + const eventListener = (...args) => savedHandler.current(...args); + + // Add event listener + emitter.addEventListener(eventName, eventListener); + + // Remove event listener on cleanup + return () => { + emitter.removeEventListener(eventName, eventListener); }; }, [eventName, emitter], // Re-run if eventName or emitter changes @@ -94,3 +133,18 @@ export function useEventEmitterState( useEventEmitter(emitter, eventName, handler); return value; } + +export function useEventTargetState( + target: EventTarget | undefined, + eventName: string, + fn: Mapper, +): T { + const [value, setValue] = useState(fn()); + const handler = useCallback((...args: any[]) => { + setValue(fn(...args)); + }, [fn]); + // re-run when the emitter changes + useEffect(handler, [target]); // eslint-disable-line react-hooks/exhaustive-deps + useEventTarget(target, eventName, handler); + return value; +} diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts index aea6e591cb..ef2b163d42 100644 --- a/test/test-utils/test-utils.ts +++ b/test/test-utils/test-utils.ts @@ -382,6 +382,7 @@ export function mkStubRoom(roomId: string = null, name: string, client: MatrixCl members: {}, getJoinRule: jest.fn().mockReturnValue(JoinRule.Invite), on: jest.fn(), + off: jest.fn(), } as unknown as RoomState, tags: {}, setBlacklistUnverifiedDevices: jest.fn(),