自定义 hooks

96 阅读1分钟

useOnReady

import { useEffect, useState } from 'react'

// 当满足某个条件时才执行(仅一次)
import { useEffect, useState } from 'react'

export function useOnReady<T>(callback: T, isReady: boolean | (() => boolean) | (() => Promise<boolean>)) {
  const [done, setDone] = useState(false)
  const [ready, setReady] = useState(false)

  useEffect(() => {
    if (typeof isReady === 'boolean') {
      setReady(isReady)
    }

    if (typeof isReady === 'function') {
      const res = isReady()

      if (typeof res === 'boolean') {
        setReady(res)
      } else if (typeof res === 'object' && typeof res.then === 'function') {
        res.then(setReady)
      }
    }
  }, [isReady])

  useEffect(() => {
    if (done) return

    if (ready) {
      if (typeof callback === 'function') {
        callback()
        setDone(true)
      }
    }
  }, [callback, done, ready])
}

useChannel

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

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

  useEffect(() => {
    const sender = new BroadcastChannel<T>(params.key, params.options)
    const receiver = new BroadcastChannel<T>(params.key, params.options)

    if (typeof params.onMessage === 'function') {
      if (params.options?.selfMonitoring) {
        receiver.addEventListener('message', params.onMessage)
      } else {
        sender.addEventListener('message', params.onMessage)
      }
    }

    senderRef.current = sender
    receiverRef.current = receiver

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

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

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

  const close = useCallback(() => {
    senderRef.current?.close()
    receiverRef.current?.close()
  }, [])

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

  return { send, close }
}