Кеш серверних даних корисний, доки він актуальний. Є три джерела змін, і кожне потребує свого механізму.
1. Зміни, зроблені самим користувачем (мутації). Після успішного запиту кеш, що залежить від змінених даних, треба оновити:
useMutation({
mutationFn: createOrder,
onSuccess: (order) => {
queryClient.invalidateQueries({ queryKey: ['orders'] }); // позначити застарілим і перезапитати
queryClient.setQueryData(['orders', order.id], order); // покласти відповідь у кеш одразу
},
});
invalidateQueries з префіксом ключа (['orders']) зачіпає всі запити, ключ яких з нього починається: списки з різними фільтрами, сторінки пагінації. Тому ієрархічні ключі (['orders', { status }], ['orders', id]) важливі з самого початку.
2. Зміни, зроблені іншими (інші користувачі, фонові задачі).
- повторний запит при поверненні на вкладку (
refetchOnWindowFocus, увімкнено за замовчуванням) і з інтервалом (refetchInterval) - простий варіант без інфраструктури; - події в реальному часі - WebSocket чи SSE (у Laravel - Reverb + Echo). Подія від сервера інвалідує чи оновлює кеш:
useEffect(() => {
const channel = echo.private(`team.${teamId}`);
channel.listen('OrderShipped', (event) => {
queryClient.setQueryData(['orders', event.order.id], event.order);
queryClient.invalidateQueries({ queryKey: ['orders'], exact: false, refetchType: 'active' });
});
return () => echo.leave(`team.${teamId}`);
}, [teamId, queryClient]);
setQueryData чи invalidateQueries:
setQueryData- коли подія містить повні актуальні дані: без додаткового запиту;invalidateQueries- коли подія лише сигналізує «щось змінилося»: менше даних у повідомленні, але запит на сервер.refetchType: 'active'перезапитує лише те, що зараз на екрані, решта оновиться при наступному використанні.
3. Застарівання за часом - staleTime. Для кожного типу даних - свій: довідники (міста, категорії) - години, список замовлень - секунди, курс валют - хвилина.
Пастки:
- шторм запитів: подія, що інвалідує широкий ключ, у тисячі відкритих вкладок одночасно - тисячі запитів на сервер. Варто передавати дані в самій події або додавати випадкову затримку;
- порядок подій: подія може прийти раніше, ніж відповідь на мутацію в цій самій вкладці, - перевіряти версію (
updated_at) перед записом у кеш; - авторизація каналів: приватні канали WebSocket мають перевіряти, чи користувач має доступ до даних, - інакше кеш отримає чужі дані.
Докладніше в документації: TanStack Query: інвалідація запитів