Files
element-call/src/useEvents.ts
T

87 lines
2.3 KiB
TypeScript
Raw Normal View History

/*
Copyright 2022-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE in the repository root for full details.
*/
import { useCallback, useEffect, useSyncExternalStore } from "react";
2022-10-14 09:40:21 -04:00
import type {
Listener,
ListenerMap,
TypedEventEmitter,
2025-03-13 13:58:43 +01:00
} from "matrix-js-sdk/lib/models/typed-event-emitter";
2022-10-14 09:40:21 -04:00
/**
* Shortcut for registering a listener on an EventTarget.
*/
2023-09-22 18:05:13 -04:00
export function useEventTarget<T extends Event>(
2022-11-28 16:15:47 -05:00
target: EventTarget | null | undefined,
eventType: string,
listener: (event: T) => void,
2023-10-11 10:42:04 -04:00
options?: AddEventListenerOptions,
2023-09-22 18:05:13 -04:00
): void {
useEffect(() => {
if (target) {
2023-06-30 16:43:28 +01:00
target.addEventListener(eventType, listener as EventListener, options);
2024-06-04 11:20:25 -04:00
return (): void =>
2023-06-30 16:43:28 +01:00
target.removeEventListener(
eventType,
listener as EventListener,
2023-10-11 10:42:04 -04:00
options,
2023-06-30 16:43:28 +01:00
);
}
}, [target, eventType, listener, options]);
2023-09-22 18:05:13 -04:00
}
/**
* Shortcut for registering a listener on a TypedEventEmitter.
*/
2023-09-22 18:05:13 -04:00
export function useTypedEventEmitter<
2022-10-14 09:40:21 -04:00
Events extends string,
Arguments extends ListenerMap<Events>,
2023-10-11 10:42:04 -04:00
T extends Events,
2022-10-14 09:40:21 -04:00
>(
emitter: TypedEventEmitter<Events, Arguments>,
eventType: T,
2023-10-11 10:42:04 -04:00
listener: Listener<Events, Arguments, T>,
2023-09-22 18:05:13 -04:00
): void {
2022-10-14 09:40:21 -04:00
useEffect(() => {
emitter.on(eventType, listener);
2024-06-04 11:20:25 -04:00
return (): void => {
2022-10-14 09:40:21 -04:00
emitter.off(eventType, listener);
};
}, [emitter, eventType, listener]);
2023-09-22 18:05:13 -04:00
}
/**
* Reactively tracks a value which is recalculated whenever the provided event
* emitter emits an event. This is useful for bridging state from matrix-js-sdk
* into React.
*/
export function useTypedEventEmitterState<
Events extends string,
Arguments extends ListenerMap<Events>,
T extends Events,
State,
>(
emitter: TypedEventEmitter<Events, Arguments>,
eventType: T,
getState: () => State,
): State {
const subscribe = useCallback(
(onChange: () => void) => {
emitter.on(eventType, onChange as Listener<Events, Arguments, T>);
return (): void => {
emitter.off(eventType, onChange as Listener<Events, Arguments, T>);
};
},
[emitter, eventType],
);
// See the React docs for useSyncExternalStore; given that we're trying to
// bridge state from an external source into React, using this hook is exactly
// what React recommends.
return useSyncExternalStore(subscribe, getState);
}