import { useState, useEffect, useCallback, useRef } from 'react'; /** * Интерфейс для кэшированных данных */ interface CacheItem { data: T; timestamp: number; } /** * Интерфейс для опций хука */ interface UseAdminCacheOptions { key: string; ttl?: number; // Время жизни кэша в миллисекундах storage?: 'memory' | 'session' | 'local'; } /** * Глобальный кэш для хранения данных в памяти */ const memoryCache = new Map>(); /** * Хук для кэширования данных в админке * @param options Опции хука * @returns Объект с функциями и состоянием */ export function useAdminCache(options: UseAdminCacheOptions) { const { key, ttl = 5 * 60 * 1000, // 5 минут по умолчанию storage = 'memory' } = options; const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(false); const timerRef = useRef(null); /** * Получение данных из кэша */ const getFromCache = useCallback((): CacheItem | null => { try { if (storage === 'memory') { return memoryCache.get(key) as CacheItem || 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 = { 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) => { 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;