Let's assume we have a scenario where we want to logout our opened application in another tab/window in the same browser in a React application or we have similar scenarios mentioned below:
// src/hooks/useBroadcastChannel.ts
import { useMemo, useEffect, useCallback, useRef, ReactNode } from "react";
export type MessageType = string | object | [] | ReactNode;
const channelInstances: { [key: string]: BroadcastChannel } = {};
const getSingletonChannelInstance = (name: string): BroadcastChannel => {
if (!channelInstances[name]) {
channelInstances[name] = new BroadcastChannel(name);
}
return channelInstances[name];
};
export function useBroadcastChannel(
channelName: string,
onMessageHandler: (message: MessageType) => void
) {
const isSubscribed = useRef(false);
const channel = useMemo(
() => getSingletonChannelInstance(channelName),
[channelName]
);
useEffect(() => {
if (!isSubscribed.current) {
channel.onmessage = (event) => onMessageHandler(event.data);
}
return () => {
if (isSubscribed.current) {
isSubscribed.current = true;
}
};
}, []);
const postMessage = useCallback(
(message: MessageType) => {
channel?.postMessage(message);
},
[channel]
);
return {
postMessage,
};
}
// /src/components/broadcast.tsx
'use client';
import { FormEvent, useState } from "react";
import { useBroadcastChannel, MessageType } from "../hooks/useBroadcastChannel";
const Broadcast = () =>{
const [msg, setMsg] = useState<MessageType>('');
const handler = (message: MessageType) =>{
console.log('message ', message?.toString())
setMsg(message);
}
const {postMessage} = useBroadcastChannel('broadcast-channel', handler)
const changeHandler = (event: FormEvent<HTMLInputElement>) => {
postMessage(event?.currentTarget?.value.toString());
};
return (
<>
<input type="text" onChange={changeHandler} />
<div>{String(msg)}</div>
</>
)
}
export default Broadcast;
Let's assume, we have a requirement for the same where we want to broadcast to the same page, for example: if from profile page we have triggered logout and want this to reflect to the other tab and i have receiving event listener on the same page. We can achieve this using 2 BroadcastChannel
, following steps can help achieve the same.
BroadcastChannel
.postMessage
.onmessage
.// src/hooks/useBroadcastChannelSamePage.ts
import { useMemo, useEffect, useCallback, useRef, ReactNode } from "react";
export type MessageType = string | object | [] | ReactNode;
type ChannelType = {
sender: BroadcastChannel;
receiver: BroadcastChannel;
};
const channelInstances: { [key: string]: BroadcastChannel } = {};
const getSingletonChannelInstance = (name: string): ChannelType => {
if (
!channelInstances[name + "_sender"] &&
!channelInstances[name + "_receiver"]
) {
channelInstances[name + "_sender"] = new BroadcastChannel(name);
channelInstances[name + "_receiver"] = new BroadcastChannel(name);
}
return {
sender: channelInstances[name + "_sender"],
receiver: channelInstances[name + "_receiver"],
};
};
export function useBroadcastChannelSamePage(
channelName: string,
onMessageHandler: (message: MessageType) => void
) {
const isSubscribed = useRef(false);
const channel = useMemo(
() => getSingletonChannelInstance(channelName),
[channelName]
);
useEffect(() => {
if (!isSubscribed.current) {
channel.receiver.onmessage = (event) => onMessageHandler(event.data);
}
return () => {
if (isSubscribed.current) {
isSubscribed.current = true;
}
};
}, []);
const postMessage = useCallback(
(message: MessageType) => {
channel?.sender?.postMessage(message);
},
[channel]
);
return {
postMessage,
};
}
// /src/components/broadcast-same-page.tsx
"use client";
import { FormEvent, useState } from "react";
import {
useBroadcastChannelSamePage,
MessageType}
from "../hooks/useBroadcastChannelSamePage";
const BroadCastPageSamePage = () => {
const [msg, setMsg] = useState<MessageType>('');
const handler = (message: MessageType) => {
setMsg(message);
};
const { postMessage } = useBroadcastChannelSamePage("broadcast-ch", handler);
const changeHandler = (event: FormEvent<HTMLInputElement>) => {
postMessage(event?.currentTarget?.value.toString());
};
return (
<>
<input type="text" onChange={changeHandler} />
<div>{String(msg)}</div>
</>
);
};
export default BroadCastPageSamePage;