import { useState, useEffect, useRef, useCallback } from 'react'
type UseSynchronizedStateOptions<StateType> = {
initialState?: StateType | (() => StateType)
export function useSynchronizedState<StateType>({
}: UseSynchronizedStateOptions<StateType>) {
const [state, setState] = useState<StateType | undefined>(initialState)
const emitterChannelRef = useRef<BroadcastChannel | null>(null)
const receiverChannelRef = useRef<BroadcastChannel | null>(null)
const lastTrackedState = useRef<StateType | undefined>(undefined)
const isFirstRender = useRef(true)
if (lastTrackedState.current === undefined && isFirstRender.current) {
lastTrackedState.current =
typeof initialState === 'function'
? (initialState as () => StateType)()
const broadcast = useCallback((message: StateType) => {
emitterChannelRef.current !== null &&
JSON.stringify(message) !== JSON.stringify(lastTrackedState.current)
lastTrackedState.current = message
emitterChannelRef.current.postMessage(message)
if (emitterChannelRef.current === null && receiverChannelRef.current === null) {
emitterChannelRef.current = new BroadcastChannel(key)
receiverChannelRef.current = new BroadcastChannel(key)
const onMessage = (event: MessageEvent<StateType>) => {
const onMessageError = () => {
console.error(`Error receiving message on channel with key: ${key}`)
receiverChannelRef.current?.addEventListener('message', onMessage)
receiverChannelRef.current?.addEventListener('messageerror', onMessageError)
isFirstRender.current = false
receiverChannelRef.current?.removeEventListener('message', onMessage)
receiverChannelRef.current?.removeEventListener('messageerror', onMessageError)
if (receiverChannelRef.current !== null && emitterChannelRef.current !== null) {
emitterChannelRef.current.close()
receiverChannelRef.current.close()
emitterChannelRef.current = null
receiverChannelRef.current = null
if (track !== undefined) broadcast(track)
return { state, broadcast }