import { useCallback, useEffect, useRef, useState } from "react"; interface UseBroadcastChannelOptions { name: string; onMessage?: (event: MessageEvent) => void; onMessageError?: (event: MessageEvent) => void; } interface UseBroadcastChannelReturn { isSupported: boolean; channel: BroadcastChannel | undefined; data: D | undefined; post: (data: P) => void; close: () => void; messageError: Event | undefined; isClosed: boolean; } function useBroadcastChannel( options: UseBroadcastChannelOptions ): UseBroadcastChannelReturn { const [isSupported, setIsSupported] = useState(false); const channelRef = useRef(undefined); const [data, setData] = useState(); const [messageError, setMessageError] = useState( undefined ); const [isClosed, setIsClosed] = useState(false); useEffect(() => { setIsSupported( typeof window !== "undefined" && "BroadcastChannel" in window ); }, []); const handleMessage = useCallback( (event: MessageEvent) => { setData(event.data as D); options.onMessage?.(event); }, [options.onMessage] ); const handleMessageError = useCallback( (event: MessageEvent) => { setMessageError(event); options.onMessageError?.(event); }, [options.onMessageError] ); useEffect(() => { if (isSupported) { const newChannel = new BroadcastChannel(options.name); channelRef.current = newChannel; newChannel.addEventListener("message", handleMessage); newChannel.addEventListener("messageerror", handleMessageError); return () => { newChannel.removeEventListener("message", handleMessage); newChannel.removeEventListener("messageerror", handleMessageError); if (!isClosed) { newChannel.close(); } channelRef.current = undefined; }; } }, [isSupported, options.name, handleMessage, handleMessageError]); const post = useCallback( (messageData: P) => { if (channelRef.current && !isClosed) { channelRef.current.postMessage(messageData); } }, [isClosed] ); const close = useCallback(() => { if (channelRef.current && !isClosed) { channelRef.current.close(); setIsClosed(true); } }, [isClosed]); return { isSupported, channel: channelRef.current, data, post, close, messageError, isClosed, }; } export default useBroadcastChannel;