diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 09e6d89e..27d81bc4 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -69,6 +69,7 @@ export * from './usePaint/usePaint'; export * from './useParallax/useParallax'; export * from './usePermission/usePermission'; export * from './usePointerLock/usePointerLock'; +export * from './usePostMessage/usePostMessage'; export * from './usePreferredColorScheme/usePreferredColorScheme'; export * from './usePreferredContrast/usePreferredContrast'; export * from './usePreferredDark/usePreferredDark'; diff --git a/src/hooks/usePostMessage/usePostMessage.demo.tsx b/src/hooks/usePostMessage/usePostMessage.demo.tsx new file mode 100644 index 00000000..ecd5c0b0 --- /dev/null +++ b/src/hooks/usePostMessage/usePostMessage.demo.tsx @@ -0,0 +1,23 @@ +import { usePostMessage } from './usePostMessage'; + +const Demo = () => { + const { postMessage } = usePostMessage((event) => { + if (location.origin === event.origin) { + console.log('MessageEvent: ', event); + } + }); + + const onClick = () => { + postMessage(window, 'Custom Message', { + targetOrigin: '*' + }); + }; + + return ( +
+ +
+ ); +}; + +export default Demo; diff --git a/src/hooks/usePostMessage/usePostMessage.ts b/src/hooks/usePostMessage/usePostMessage.ts new file mode 100644 index 00000000..75a53c1e --- /dev/null +++ b/src/hooks/usePostMessage/usePostMessage.ts @@ -0,0 +1,72 @@ +import { useEffect } from 'react'; + +/** The possible entity types */ +type PossibleEntity = Window | Worker | MessagePort; + +/** The use post message entity arguments */ +type PostMessageEntityArguments = Entity['postMessage'] extends ( + message: unknown, + ...rest: infer Rest +) => void + ? Rest + : never; + +/** The use post message return type */ +export interface UsePostMessageReturn { + postMessage: ( + targetSource: Entity, + message: MessagePayload, + ...args: PostMessageEntityArguments + ) => void; +} + +/** + * @name usePostMessage + * @description - Hook that allows you to use `postMessage` function + * @category Browser + * + * @overload + * @template MessagePayload The message data type + * @param {(message: MessageEvent) => void} onMessage callback to get received message event + * @returns {UsePostMessageReturn} An object with a patched `postMethod` function + * + * @example + * const { postMessage } = usePostMessage(); + */ +export const usePostMessage = ( + onMessage?: (message: MessageEvent) => void +): UsePostMessageReturn => { + const postMessage = ( + targetSource: Entity, + message: MessagePayload, + ...args: unknown[] + ) => { + if (targetSource instanceof Window) { + targetSource.postMessage(message, ...(args as PostMessageEntityArguments)); + } else if (targetSource instanceof Worker) { + targetSource.postMessage(message, ...(args as PostMessageEntityArguments)); + } else { + targetSource.postMessage(message, ...(args as PostMessageEntityArguments)); + } + }; + + useEffect(() => { + const controller = new AbortController(); + + window.addEventListener( + 'message', + (event: MessageEvent) => { + onMessage?.(event); + }, + { signal: controller.signal } + ); + + return () => { + controller.abort(); + }; + }, [onMessage]); + + return { + postMessage + }; +};