「hooks」封装 broadcast-channel 钩子

119 阅读1分钟

broadcast-channel是一个跨浏览器页签通信工具(不同的浏览器不能通信);为了方便在 react 中使用,将其封装为钩子

代码实现

pnpm i broadcast-channel
import type { BroadcastChannelOptions } from 'broadcast-channel'
import { BroadcastChannel } from 'broadcast-channel'
import { useCallback, useEffect, useRef, useState } from 'react'

export function useChannel<T = any>(
  key: string,
  options?: BroadcastChannelOptions & {
    // 是否监听自己发送的消息
    // 默认为 false
    selfMonitoring?: boolean
  },
) {
  // 使用两个实例
  // 使得当前窗口也可以接受自己发送的消息
  const senderRef = useRef<BroadcastChannel<T>>()
  const receiverRef = useRef<BroadcastChannel<T>>()

  const [message, setMessage] = useState<T>()

  useEffect(() => {
    if (!key.trim()) {
      throw new Error('useChannel: key is required')
    }

    const sender = new BroadcastChannel<T>(key, options)
    const receiver = new BroadcastChannel<T>(key, options)

    if (options?.selfMonitoring) {
      receiver.addEventListener('message', (e) => {
        setMessage(e)
      })
    } else {
      sender.addEventListener('message', (e) => {
        setMessage(e)
      })
    }

    senderRef.current = sender
    receiverRef.current = receiver

    return () => {
      if (senderRef.current && receiverRef.current) {
        senderRef.current.close()
        receiverRef.current.close()
      }
    }
  }, [key, options])

  const send = useCallback((payload: T) => {
    if (!senderRef.current) return

    senderRef.current.postMessage(payload)
  }, [])

  useEffect(() => {
    return () => {
      senderRef.current?.close()
      receiverRef.current?.close()
    }
  }, [])

  return [message, send] as const
}

使用

function App() {
  const [msg, send] = useChannel('my-channel', { selfMonitoring: true })

  useEffect(() => {
    console.log('msg', msg)
  }, [msg])

  return (
    <>
      <button type='button' onClick={() => send(`${Date.now()}`)}>
        send
      </button>
    </>
  )
}