妥妥干货,通过React实现减少请求次数

74 阅读1分钟

话不多说,先上代码

import { useState, useMemo, useEffect, Dispatch, SetStateAction } from "react"

export type AsyncResult<T> = [T | undefined, boolean, any, Dispatch<SetStateAction<T | undefined>>]

export interface PromiseCacheOptions<K, V> {
  data: Map<K, V>
  promiseFn: (key: K) => Promise<V>
  keyFn: (data: V, key: K) => K
}


export class PromiseCache<K, V> {
  private cache: Map<K, Promise<V>> = new Map()
  constructor(private options: PromiseCacheOptions<K, V>) {
  }

  async get(key: K): Promise<V> {
    if (this.options.data.has(key)) {
      return Promise.resolve(this.options.data.get(key)!!)
    }
    if (this.cache.has(key)) {
      return this.cache.get(key)!!
    }
    const result = this.options.promiseFn(key).then((res: V) => {
      this.options.data.set(key, res)
      return res
    })
    this.cache.set(key, result)
    return result
  }
}

export function useAsync<T>(fn: () => Promise<T>, dependency?: React.DependencyList): AsyncResult<T> {
  const [value, setValue] = useState<T | undefined>()
  const [error, setError] = useState<any>(null)
  const [loading, setLoading] = useState<boolean>(true)
  const state = useMemo<{ count: number }>(() => { return { count: 0 } }, [])

  const dep = dependency ?? [fn]
  useEffect(() => {
      setLoading(true);
      setError(null);
      let asyncFn = (count: number) => {
          fn().then(r => {
              if (count === state.count) {
                  setValue(r)
              }
          }).catch(e => {
              if (count === state.count) {
                  setError(e)
              }
          }).finally(() => {
              if (count === state.count) {
                  setLoading(false)
              }
          })
      }
      asyncFn(state.count + 1)
      state.count++

  }, dep)

  return [value, loading, error, setValue]

}
import { PromiseCache, useAsync } from '@/lib/cache';

const Context = new PromiseCache<string, CacheType | undefined>({
  data: new Map(),
  promiseFn: (key: string) => {
    const [project_id, type_id] = key.split('&')
    return Promise().then(v => {
      return v.data
    })
  },
  keyFn: () => {
      return ''
  }
})

function method(props: Props) {
    const [data, loading, err] = useAsync(async () => {
    if (project_id && typeId!== undefined) {
      const res = await Context.get(`id`)
      return res.data
    }
    return undefined
}, [id])
return (
    <></>
)
}

该方法通过map去存储promise请求,每次请求通过调用PromiseCache类的get方法先判断是否存在key来判断是否需要发送请求。

有小伙伴尝试的话,也可以试试改写成vue形式。