# 🔄 ПРАВИЛА КЕШИРОВАНИЯ GRAPHQL И FETCHPOLICY > **Цель:** Обеспечить корректное кеширование GraphQL данных и предотвратить проблемы синхронизации между компонентами ## 📋 **ОСНОВНЫЕ ПРИНЦИПЫ КЕШИРОВАНИЯ** ### 1. **СИНХРОНИЗАЦИЯ СВЯЗАННЫХ КОМПОНЕНТОВ** ```typescript // ✅ ПРАВИЛЬНО - одинаковые настройки для связанных данных const masterComponent = useQuery(GET_WAREHOUSE_STATS, { fetchPolicy: 'cache-and-network', pollInterval: 30000, errorPolicy: 'all', }) const detailComponent = useQuery(GET_SUPPLIES_DETAILS, { fetchPolicy: 'cache-and-network', // ← ОБЯЗАТЕЛЬНО то же самое! pollInterval: 30000, // ← ОБЯЗАТЕЛЬНО то же самое! errorPolicy: 'all', // ← ОБЯЗАТЕЛЬНО то же самое! }) ``` ```typescript // ❌ НЕПРАВИЛЬНО - разные настройки создают рассинхронизацию const masterComponent = useQuery(GET_WAREHOUSE_STATS, { fetchPolicy: 'cache-and-network', }) const detailComponent = useQuery(GET_SUPPLIES_DETAILS, { // default fetchPolicy: 'cache-first' - СОЗДАЁТ ПРОБЛЕМУ! }) ``` ### 2. **ОБЯЗАТЕЛЬНЫЕ FETCH POLICIES ПО ТИПАМ КОМПОНЕНТОВ** #### **Dashboard и Статистика** ```typescript useQuery(DASHBOARD_QUERY, { fetchPolicy: 'cache-and-network', // Всегда актуальные данные pollInterval: 30000, // Обновление каждые 30 сек errorPolicy: 'all', // Показывать частичные данные при ошибках }) ``` #### **Связанные компоненты (Master-Detail)** ```typescript // Все компоненты одной функциональности ДОЛЖНЫ иметь идентичные настройки const sharedQueryOptions = { fetchPolicy: 'cache-and-network' as const, pollInterval: 30000, errorPolicy: 'all' as const, } // Применение во всех связанных компонентах useQuery(MASTER_QUERY, sharedQueryOptions) useQuery(DETAIL_QUERY, sharedQueryOptions) useQuery(STATS_QUERY, sharedQueryOptions) ``` #### **Списки и таблицы** ```typescript useQuery(LIST_QUERY, { fetchPolicy: 'cache-and-network', // Актуальные данные pollInterval: 60000, // 1 минута для списков notifyOnNetworkStatusChange: true, // Показывать статус загрузки }) ``` #### **Редко изменяющиеся данные** ```typescript useQuery(STATIC_DATA_QUERY, { fetchPolicy: 'cache-first', // Кеш приоритетен pollInterval: 300000, // 5 минут errorPolicy: 'ignore', // Не показывать ошибки для статичных данных }) ``` --- ## 🏢 **СПЕЦИФИЧЕСКИЕ ПРАВИЛА ДЛЯ ФУЛФИЛМЕНТА** ### **ГРУППЫ СИНХРОНИЗИРОВАННЫХ КОМПОНЕНТОВ** #### **Группа 1: Складская статистика** ```typescript // Все эти компоненты ДОЛЖНЫ использовать одинаковые настройки: const warehouseStatsOptions = { fetchPolicy: 'cache-and-network' as const, pollInterval: 30000, errorPolicy: 'all' as const, } // 1. Главный dashboard (/fulfillment-warehouse) useQuery(GET_WAREHOUSE_STATS, warehouseStatsOptions) // 2. Подраздел расходников (/fulfillment-warehouse/supplies) useQuery(GET_SUPPLIES_STATS, warehouseStatsOptions) // 3. Раздел услуг (/services) - вкладка "Расходники" useQuery(GET_SERVICE_SUPPLIES, warehouseStatsOptions) ``` #### **Группа 2: История поставок** ```typescript const suppliesHistoryOptions = { fetchPolicy: 'cache-and-network' as const, pollInterval: 30000, errorPolicy: 'all' as const, } // 1. Основные поставки (главная таблица) useQuery(GET_MY_FULFILLMENT_SUPPLIES, suppliesHistoryOptions) // 2. Детали поставок (раскрывающиеся строки) useQuery(GET_MY_FULFILLMENT_CONSUMABLE_SUPPLIES, suppliesHistoryOptions) ``` --- ## 🚨 **КРИТИЧЕСКИЕ АНТИ-ПАТТЕРНЫ** ### **❌ НИКОГДА НЕ ДЕЛАЙТЕ:** #### **1. Разные fetchPolicy для связанных данных** ```typescript // ❌ СОЗДАЁТ РАССИНХРОНИЗАЦИЮ const warehouse = useQuery(GET_WAREHOUSE, { fetchPolicy: 'cache-and-network' }) const supplies = useQuery(GET_SUPPLIES, {}) // default cache-first // Результат: warehouse показывает новые данные, supplies - старые ``` #### **2. Разные pollInterval для одной функциональности** ```typescript // ❌ СОЗДАЁТ НЕСОГЛАСОВАННОСТЬ const stats = useQuery(GET_STATS, { pollInterval: 30000 }) const details = useQuery(GET_DETAILS, { pollInterval: 60000 }) // Результат: stats обновляется чаще details - значения не совпадают ``` #### **3. Смешивание cache-first и cache-and-network** ```typescript // ❌ КЛАССИЧЕСКАЯ ОШИБКА const mainData = useQuery(MAIN_QUERY, { fetchPolicy: 'cache-and-network' }) const relatedData = useQuery(RELATED_QUERY, { fetchPolicy: 'cache-first' }) // Результат: mainData актуальные, relatedData устаревшие ``` #### **4. Игнорирование ошибок в критических компонентах** ```typescript // ❌ СКРЫВАЕТ ПРОБЛЕМЫ useQuery(CRITICAL_DATA_QUERY, { errorPolicy: 'ignore', // Не показывает ошибки когда нужно! }) ``` --- ## 📊 **ПРАВИЛА ПО ТИПАМ ДАННЫХ** ### **СТАТИСТИКА И DASHBOARD** ```typescript const dashboardOptions = { fetchPolicy: 'cache-and-network', // Всегда актуальные данные pollInterval: 30000, // 30 сек - критическая актуальность errorPolicy: 'all', // Показывать частичные данные notifyOnNetworkStatusChange: true, // Показывать индикатор обновления } ``` ### **ИНВЕНТАРЬ И ОСТАТКИ** ```typescript const inventoryOptions = { fetchPolicy: 'cache-and-network', // Остатки должны быть точными pollInterval: 30000, // Частое обновление errorPolicy: 'all', // Критические данные } ``` ### **ИСТОРИЯ И ЛОГИ** ```typescript const historyOptions = { fetchPolicy: 'cache-first', // История не меняется часто pollInterval: 120000, // 2 минуты достаточно errorPolicy: 'all', // Показывать что есть } ``` ### **СПРАВОЧНИКИ И КАТАЛОГИ** ```typescript const catalogOptions = { fetchPolicy: 'cache-first', // Справочники стабильны pollInterval: 300000, // 5 минут errorPolicy: 'ignore', // Не критично } ``` --- ## 🔧 **ПРАКТИЧЕСКИЕ ПРИМЕРЫ ИСПРАВЛЕНИЙ** ### **ПРИМЕР 1: Рассинхронизация карточек статистики** **❌ Проблема:** ```typescript // Главный dashboard const warehouseStats = useQuery(GET_WAREHOUSE_STATS, { fetchPolicy: 'cache-and-network', }) // Подраздел supplies const suppliesStats = useQuery(GET_SUPPLIES_STATS, { // default fetchPolicy: 'cache-first' - ПРОБЛЕМА! }) // Результат: карточка "РАСХОДНИКИ ФУЛФИЛМЕНТА" = 130, карточка "ОСТАТОК" = 160 ``` **✅ Решение:** ```typescript // Общие настройки для всех статистических компонентов const statsOptions = { fetchPolicy: 'cache-and-network' as const, pollInterval: 30000, errorPolicy: 'all' as const, } // Применяем везде одинаково const warehouseStats = useQuery(GET_WAREHOUSE_STATS, statsOptions) const suppliesStats = useQuery(GET_SUPPLIES_STATS, statsOptions) ``` ### **ПРИМЕР 2: История поставок не синхронизируется с основной таблицей** **❌ Проблема:** ```typescript // Основная таблица const supplies = useQuery(GET_SUPPLIES, { fetchPolicy: 'cache-and-network' }) // История в раскрывающихся строках const history = useQuery(GET_SUPPLY_HISTORY, {}) // default cache-first // Результат: основная таблица обновилась, история показывает старые данные ``` **✅ Решение:** ```typescript const syncedOptions = { fetchPolicy: 'cache-and-network', pollInterval: 30000, } const supplies = useQuery(GET_SUPPLIES, syncedOptions) const history = useQuery(GET_SUPPLY_HISTORY, syncedOptions) ``` --- ## 🎯 **ЧЕКЛИСТ НАСТРОЙКИ КЕШИРОВАНИЯ** ### **Перед релизом компонента:** - [ ] Определили группу связанных компонентов - [ ] Выбрали единую fetchPolicy для группы - [ ] Установили одинаковые pollInterval значения - [ ] Настроили errorPolicy в соответствии с критичностью - [ ] Протестировали синхронизацию данных ### **При обнаружении рассинхронизации:** - [ ] Проверили fetchPolicy всех связанных запросов - [ ] Сравнили pollInterval значения - [ ] Убедились в использовании одних таблиц БД - [ ] Проверили последовательность обновления данных --- ## 📈 **МОНИТОРИНГ И ОТЛАДКА** ### **DEBUG ЛОГИ ДЛЯ КЕШИРОВАНИЯ** ```typescript // Добавляйте в компоненты для контроля кеширования useQuery(QUERY, { fetchPolicy: 'cache-and-network', onCompleted: (data) => { console.log('CACHE DEBUG:', { query: 'QUERY_NAME', timestamp: new Date().toISOString(), dataLength: data?.items?.length, source: 'network/cache', }) }, onError: (error) => { console.error('CACHE ERROR:', { query: 'QUERY_NAME', error: error.message, networkError: error.networkError?.message, }) }, }) ``` ### **ПРОВЕРКА СИНХРОНИЗАЦИИ** ```typescript // Контрольные точки для проверки консистентности useEffect(() => { if (masterData && detailData) { const masterValue = masterData.total const detailValue = detailData.reduce((sum, item) => sum + item.value, 0) if (Math.abs(masterValue - detailValue) > 0) { console.error('🚨 CACHE SYNC ERROR:', { component: 'ComponentName', master: masterValue, detail: detailValue, diff: masterValue - detailValue, }) } } }, [masterData, detailData]) ``` --- ## 🚀 **РЕКОМЕНДАЦИИ ПО ПРОИЗВОДИТЕЛЬНОСТИ** ### **ОПТИМИЗАЦИЯ POLLING** ```typescript // Адаптивные интервалы в зависимости от активности пользователя const useAdaptivePolling = () => { const [isActive, setIsActive] = useState(true) useEffect(() => { const handleVisibilityChange = () => { setIsActive(!document.hidden) } document.addEventListener('visibilitychange', handleVisibilityChange) return () => document.removeEventListener('visibilitychange', handleVisibilityChange) }, []) return { pollInterval: isActive ? 30000 : 120000, // Реже обновляем неактивные вкладки } } ``` ### **УСЛОВНОЕ КЕШИРОВАНИЕ** ```typescript // Разные стратегии для разных сценариев const getCachePolicy = (dataType: string) => { switch (dataType) { case 'critical-stats': return { fetchPolicy: 'cache-and-network', pollInterval: 30000 } case 'user-lists': return { fetchPolicy: 'cache-and-network', pollInterval: 60000 } case 'reference-data': return { fetchPolicy: 'cache-first', pollInterval: 300000 } default: return { fetchPolicy: 'cache-first' } } } ``` **Следование этим правилам обеспечит стабильное кеширование и синхронизацию GraphQL данных!** 🚀