192 lines
5.5 KiB
TypeScript
192 lines
5.5 KiB
TypeScript
import { useState, useEffect, useCallback, useRef } from 'react';
|
||
|
||
/**
|
||
* Интерфейс для кэшированных данных
|
||
*/
|
||
interface CacheItem<T> {
|
||
data: T;
|
||
timestamp: number;
|
||
}
|
||
|
||
/**
|
||
* Интерфейс для опций хука
|
||
*/
|
||
interface UseAdminCacheOptions {
|
||
key: string;
|
||
ttl?: number; // Время жизни кэша в миллисекундах
|
||
storage?: 'memory' | 'session' | 'local';
|
||
}
|
||
|
||
/**
|
||
* Глобальный кэш для хранения данных в памяти
|
||
*/
|
||
const memoryCache = new Map<string, CacheItem<any>>();
|
||
|
||
/**
|
||
* Хук для кэширования данных в админке
|
||
* @param options Опции хука
|
||
* @returns Объект с функциями и состоянием
|
||
*/
|
||
export function useAdminCache<T>(options: UseAdminCacheOptions) {
|
||
const {
|
||
key,
|
||
ttl = 5 * 60 * 1000, // 5 минут по умолчанию
|
||
storage = 'memory'
|
||
} = options;
|
||
|
||
const [data, setData] = useState<T | null>(null);
|
||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||
const timerRef = useRef<NodeJS.Timeout | null>(null);
|
||
|
||
/**
|
||
* Получение данных из кэша
|
||
*/
|
||
const getFromCache = useCallback((): CacheItem<T> | null => {
|
||
try {
|
||
if (storage === 'memory') {
|
||
return memoryCache.get(key) as CacheItem<T> || null;
|
||
} else if (storage === 'session') {
|
||
const item = sessionStorage.getItem(`admin_cache_${key}`);
|
||
return item ? JSON.parse(item) : null;
|
||
} else if (storage === 'local') {
|
||
const item = localStorage.getItem(`admin_cache_${key}`);
|
||
return item ? JSON.parse(item) : null;
|
||
}
|
||
return null;
|
||
} catch (error) {
|
||
console.error('Ошибка при получении данных из кэша:', error);
|
||
return null;
|
||
}
|
||
}, [key, storage]);
|
||
|
||
/**
|
||
* Сохранение данных в кэш
|
||
*/
|
||
const saveToCache = useCallback((value: T) => {
|
||
try {
|
||
const cacheItem: CacheItem<T> = {
|
||
data: value,
|
||
timestamp: Date.now()
|
||
};
|
||
|
||
if (storage === 'memory') {
|
||
memoryCache.set(key, cacheItem);
|
||
} else if (storage === 'session') {
|
||
sessionStorage.setItem(`admin_cache_${key}`, JSON.stringify(cacheItem));
|
||
} else if (storage === 'local') {
|
||
localStorage.setItem(`admin_cache_${key}`, JSON.stringify(cacheItem));
|
||
}
|
||
|
||
// Устанавливаем таймер для очистки кэша
|
||
if (timerRef.current) {
|
||
clearTimeout(timerRef.current);
|
||
}
|
||
|
||
timerRef.current = setTimeout(() => {
|
||
invalidateCache();
|
||
}, ttl);
|
||
} catch (error) {
|
||
console.error('Ошибка при сохранении данных в кэш:', error);
|
||
}
|
||
}, [key, ttl, storage]);
|
||
|
||
/**
|
||
* Инвалидация кэша
|
||
*/
|
||
const invalidateCache = useCallback(() => {
|
||
try {
|
||
if (storage === 'memory') {
|
||
memoryCache.delete(key);
|
||
} else if (storage === 'session') {
|
||
sessionStorage.removeItem(`admin_cache_${key}`);
|
||
} else if (storage === 'local') {
|
||
localStorage.removeItem(`admin_cache_${key}`);
|
||
}
|
||
|
||
if (timerRef.current) {
|
||
clearTimeout(timerRef.current);
|
||
timerRef.current = null;
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка при инвалидации кэша:', error);
|
||
}
|
||
}, [key, storage]);
|
||
|
||
/**
|
||
* Проверка актуальности кэша
|
||
*/
|
||
const isCacheValid = useCallback((): boolean => {
|
||
const cacheItem = getFromCache();
|
||
if (!cacheItem) return false;
|
||
|
||
const now = Date.now();
|
||
return now - cacheItem.timestamp < ttl;
|
||
}, [getFromCache, ttl]);
|
||
|
||
/**
|
||
* Загрузка данных из кэша при монтировании компонента
|
||
*/
|
||
useEffect(() => {
|
||
const loadFromCache = () => {
|
||
const cacheItem = getFromCache();
|
||
if (cacheItem && isCacheValid()) {
|
||
setData(cacheItem.data);
|
||
}
|
||
};
|
||
|
||
loadFromCache();
|
||
|
||
// Очистка таймера при размонтировании компонента
|
||
return () => {
|
||
if (timerRef.current) {
|
||
clearTimeout(timerRef.current);
|
||
}
|
||
};
|
||
}, [getFromCache, isCacheValid]);
|
||
|
||
/**
|
||
* Установка данных с сохранением в кэш
|
||
*/
|
||
const setDataWithCache = useCallback((value: T) => {
|
||
setData(value);
|
||
saveToCache(value);
|
||
}, [saveToCache]);
|
||
|
||
/**
|
||
* Загрузка данных с использованием функции загрузки
|
||
*/
|
||
const loadData = useCallback(async (loadFn: () => Promise<T>) => {
|
||
try {
|
||
// Проверяем наличие данных в кэше
|
||
if (isCacheValid()) {
|
||
const cacheItem = getFromCache();
|
||
if (cacheItem) {
|
||
setData(cacheItem.data);
|
||
return cacheItem.data;
|
||
}
|
||
}
|
||
|
||
// Если данных в кэше нет или они устарели, загружаем новые
|
||
setIsLoading(true);
|
||
const newData = await loadFn();
|
||
setDataWithCache(newData);
|
||
setIsLoading(false);
|
||
return newData;
|
||
} catch (error) {
|
||
console.error('Ошибка при загрузке данных:', error);
|
||
setIsLoading(false);
|
||
return null;
|
||
}
|
||
}, [getFromCache, isCacheValid, setDataWithCache]);
|
||
|
||
return {
|
||
data,
|
||
isLoading,
|
||
setData: setDataWithCache,
|
||
invalidateCache,
|
||
loadData
|
||
};
|
||
}
|
||
|
||
export default useAdminCache;
|