Skip to content

Instantly share code, notes, and snippets.

@KristofferEriksson
Created February 5, 2024 09:20
Show Gist options
  • Save KristofferEriksson/e4c4cd9bcbe8e4a128eb5976fecfb7c9 to your computer and use it in GitHub Desktop.
Save KristofferEriksson/e4c4cd9bcbe8e4a128eb5976fecfb7c9 to your computer and use it in GitHub Desktop.
A React hook that allows you to send and receive messages between browser tabs or windows
import { useCallback, useEffect, useRef, useState } from "react";
interface UseBroadcastChannelOptions {
name: string;
onMessage?: (event: MessageEvent) => void;
onMessageError?: (event: MessageEvent) => void;
}
interface UseBroadcastChannelReturn<D, P> {
isSupported: boolean;
channel: BroadcastChannel | undefined;
data: D | undefined;
post: (data: P) => void;
close: () => void;
messageError: Event | undefined;
isClosed: boolean;
}
function useBroadcastChannel<D, P>(
options: UseBroadcastChannelOptions
): UseBroadcastChannelReturn<D, P> {
const [isSupported, setIsSupported] = useState<boolean>(false);
const channelRef = useRef<BroadcastChannel | undefined>(undefined);
const [data, setData] = useState<D | undefined>();
const [messageError, setMessageError] = useState<Event | undefined>(
undefined
);
const [isClosed, setIsClosed] = useState<boolean>(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;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment