在写React 项目业务过程中,经常写一些 相似的代码逻辑。
比如我需要在进入页面请求接口数据

传统上我们请求接口获取数据,大概类似这样:

  /** 数据 */
  const [detail, setDetail] = useState(defaultData)
  /** 请求数据 */
  const mutate = async () => {
    try {
      const { data } = await config.getData({ ...params })
      setDetail(data)
    } catch (error) {}
  }

  useEffect(() => {
    mutate()
  }, [])

对于这类相关的业务逻辑,我写了一个自定义hooks

/** hooks - 对于简单的获取数据方法的一个封装 */
import { useState, useRef, useEffect, useMemo } from 'react'

interface IMutateOption<T> {
  /** 默认数据 */
  defaultData?: T

  /** 额外传递参数 */
  params?: Record<string, any>

  /** 初始是否发出请求 */
  initRequest?: boolean

  /** 获取数据 */
  getData: (data: Record<string, any>) => Promise<{ data?: T }>

  /** 数据格式化 */
  dataFormat?(data?: T): any

  /** 请求成功后的回调 */
  successCallback?(data?: T): void
}

export function useMutate<T>(config: IMutateOption<T>) {
  const { defaultData, params, initRequest = true } = config

  /** 数据 */
  const [detail, setDetail] = useState(defaultData)
  /** 是否初始化 */
  const isInit = useRef(initRequest)

  let loading = false

  /** 请求数据 */
  const mutate = async () => {
    try {
      loading = true

      const { data } = await config.getData({ ...params })
      const newData = config.dataFormat ? config.dataFormat(data) : data

      setDetail(newData)
      config?.successCallback?.(newData)
      return newData
    } catch (error) {
      setDetail({})
    } finally {
      loading = false
    }
  }

  /** 初始请求 */
  useEffect(() => {
    if (!isInit.current) {
      isInit.current = true
    } else {
      mutate()
    }
  }, [])

  /** 暂无数据 */
  const noData = useMemo(() => {
    if (detail !== undefined) {
      if (detail instanceof Array) {
        return detail.length === 0
      } else if (detail instanceof Object) {
        return Object.keys(detail).length === 0
      }
    }
    return false
  }, [detail])

  return { mutate, detail, setDetail, noData, loading }
}

获取数据类似逻辑简单封装