cache.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import { useEffect, useRef, useState } from "react";
  2. import { getApiCache, getGlobalStorage } from "./storage";
  3. interface UseSWCOPtions<T> {
  4. onError?: (e: unknown) => void;
  5. onLoad?: (data: T) => void;
  6. cacheOnly?: boolean;
  7. cacheTimeout?: number;
  8. }
  9. // 该hook用于在组件中通过key和异步action获取数据,并自动处理加载、错误和数据状态
  10. export function useSWC<T>(key: string, action: () => Promise<T>, options?: UseSWCOPtions<T>) {
  11. const optionsRef = useRef(options);
  12. optionsRef.current = options;
  13. const [data, setData] = useState<T | null | undefined>(() => {
  14. try {
  15. const data = JSON.parse(getApiCache().getString(`$swc-${key}`) || "null") as T;
  16. if (!data) {
  17. return undefined;
  18. }
  19. let ttl = optionsRef.current?.cacheTimeout || MAX_CACHE_TIME;
  20. // @ts-ignore
  21. let cacheTime = data.$__cacheTime;
  22. if (Date.now() - cacheTime > ttl * 1000) {
  23. return undefined;
  24. }
  25. return data;
  26. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  27. } catch (_) {
  28. return undefined;
  29. }
  30. });
  31. const [loading, setLoading] = useState<boolean>(true);
  32. const [error, setError] = useState<any>(null);
  33. const actionRef = useRef(action);
  34. actionRef.current = action;
  35. const dataRef = useRef(data);
  36. dataRef.current = data;
  37. const keyRef = useRef(key);
  38. useEffect(() => {
  39. let isMounted = true;
  40. if (dataRef.current && optionsRef.current?.cacheOnly) {
  41. return;
  42. }
  43. setLoading(true);
  44. setError(null);
  45. const options = optionsRef.current;
  46. const key = keyRef.current;
  47. actionRef.current()
  48. .then((result) => {
  49. // @ts-ignore
  50. result.$__cacheTime = Date.now();
  51. result && getApiCache().set(`$swc-${key}`, JSON.stringify(result));
  52. if (isMounted) {
  53. options?.onLoad?.(result);
  54. setData(result);
  55. setLoading(false);
  56. }
  57. })
  58. .catch((err) => {
  59. if (isMounted) {
  60. options?.onError?.(err);
  61. setError(err);
  62. setLoading(false);
  63. }
  64. });
  65. return () => {
  66. isMounted = false;
  67. };
  68. }, []);
  69. return { data, loading, error };
  70. }
  71. export const MAX_CACHE_TIME = 99999999999999;
  72. let lastClearTime = parseInt(getGlobalStorage().getString("last_clear_time") || "0");
  73. const clearTimeout = 86400 * 3 * 1000;
  74. // 开启一直 30 分钏的定时器,用于清理缓存
  75. setInterval(() => {
  76. let now = Date.now();
  77. if (now - lastClearTime < clearTimeout) {
  78. lastClearTime = now;
  79. const globalStorage = getGlobalStorage();
  80. const caches = getApiCache();
  81. caches.getAllKeys().forEach(key => {
  82. try {
  83. let {$__cacheTime} = JSON.parse(caches.getString(key) || `{"$__cacheTime": ${MAX_CACHE_TIME}}`);
  84. if (now - $__cacheTime > clearTimeout) {
  85. caches.remove(key);
  86. }
  87. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  88. } catch (_e) {
  89. }
  90. });
  91. globalStorage.set("last_clear_time", now+"");
  92. }
  93. }, 60 * 15 * 1000);