JeromeD3 / react-hooks

my hooks
0 stars 0 forks source link

useRequest缓存 --- 数据共享 #6

Open JeromeD3 opened 1 year ago

JeromeD3 commented 1 year ago
JeromeD3 commented 1 year ago

数据共享肯定得找个东西把数据存放才能共享啦

cachePromise

创建一个map用来存同一个keypromise请求函数 --> 这里是缓存请求函数

const cachePromise = new Map();

const getCachePromise = cacheKey => {
  return cachePromise.get(cacheKey);
};

const setCachePromise = (cacheKey, promise) => {
  cachePromise.set(cacheKey, promise);
  promise.then(res => {
    cachePromise.delete(cacheKey);
    return res;
  }).catch(() => {
    cachePromise.delete(cacheKey);
  });
};

export { getCachePromise, setCachePromise };

接着用发布订阅模式定义一个函数,规定了什么时候缓存这些请求,什么时候执行这些请求

cacheSubscribe

const listeners = {};

const trigger = (key, data) => {
  if (listeners[key]) {
    listeners[key].forEach(item => item(data));
  }
};

const subscribe = (key, listener) => {
  if (!listeners[key]) {
    listeners[key] = [];
  }

  listeners[key].push(listener);
  return function unsubscribe() {
    const index = listeners[key].indexOf(listener);
    listeners[key].splice(index, 1);
  };
};
export { trigger, subscribe };
JeromeD3 commented 1 year ago

有了上面两大函数,我们可以修改useCachePlugin,新增代码如下:

useCachePlugin

import { useRef } from 'react';
import * as cache from '../utils/cache';
import useCreation from '../../../useCreation';
+import * as cachePromise from '../utils/cachePromise';
+import * as cacheSubscribe from '../utils/cacheSubscribe';
const useCachePlugin = (fetchInstance, {
  cacheKey,
  staleTime = 0,
  cacheTime = 5 * 60 * 1000,
  setCache: customSetCache,
  getCache: customGetCache
}) => {
+ const unSubscribeRef = useRef();
+ const currentPromiseRef = useRef();
  const _setCache = (key, cachedData) => {
    if (customSetCache) {
      customSetCache(cachedData);
    } else {
      cache.setCache(key, cacheTime, cachedData);
    }
+   cacheSubscribe.trigger(key, cachedData.data);
  };

  const _getCache = (key, params) => {
    if (customGetCache) {
      return customGetCache(params);
    }
    return cache.getCache(key);
  };
  useCreation(() => {
    if (!cacheKey) {
      return;
    }
    const cacheData = _getCache(cacheKey);
    if (cacheData && Object.hasOwnProperty.call(cacheData, 'data')) {
      fetchInstance.state.data = cacheData.data;
      fetchInstance.state.params = cacheData.params;
      if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
        fetchInstance.state.loading = false;
      }
    }
  })
  if (!cacheKey) {
    return {};
  }

  return {
    onBefore: params => {
      const cacheData = _getCache(cacheKey, params);
      if (!cacheData || !Object.hasOwnProperty.call(cacheData, 'data')) {
        return {};
      }
      if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
        return {
          loading: false,
          data: cacheData?.data,
          returnNow: true
        };
      } else {
        return {
          data: cacheData?.data
        };
      }
    },
+   onRequest: (service, args) => {
+     let servicePromise = cachePromise.getCachePromise(cacheKey);
+     if (servicePromise && servicePromise !== currentPromiseRef.current) {
+       return {
+         servicePromise
+       };
+     }
+     servicePromise = service(...args);
+     currentPromiseRef.current = servicePromise;
+     cachePromise.setCachePromise(cacheKey, servicePromise);
+     return {
+       servicePromise
+     };
+   },
    onSuccess: (data, params) => {
      if (cacheKey) {
        _setCache(cacheKey, {
          data,
          params,
          time: new Date().getTime()
        });
+       unSubscribeRef.current = cacheSubscribe.subscribe(cacheKey, d => {
+         fetchInstance.setState({
+           data: d
+         });
+       });
+     }
    }
  };
};
export default useCachePlugin;
JeromeD3 commented 1 year ago

前提:map存的是结果,订阅函数存的是请求函数。

实现原理

  1. 一开始声明一个ref 用来表示当前的请求,其实也可以理解为上一次请求,因为不刷新的情况下,他保存的是上一次请求函数。
  2. 然后执行缓存promise请求结果,这样其他组件就能共享其他组件缓存的最新数据。那其他组件的缓存是如何保存的呢?没有缓存函数就不执行咯~
  3. 我们去缓存中拿对应key的promise,如果有,且与上一次的请求不一样,(说明其他的组件需要更新了,直接返回缓存的)就直接返回给当前实例,实例拿到后直接请求。
  4. 那请求结果一样呢?那就执行当前函数,获取最新的数据,然后赋值给ref,这样ref就是最新的了,下一次请求如果不一样就继续更新。
  5. 缓存请求结果
  6. 当请求成功的时候,直接缓存请求函数