← Назад к вопросам
Как происходит кэширование данных в React Query?
2.0 Middle🔥 241 комментариев
#React#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Кэширование данных в React Query (TanStack Query)
React Query (теперь TanStack Query) - это библиотека для управления состоянием серверных данных с умным кэшированием. Давайте разберемся как оно работает.
Основная концепция: Query Keys и Cache
React Query хранит данные в памяти по ключам запроса:
import { useQuery } from '@tanstack/react-query';
function useUser(userId) {
return useQuery({
queryKey: ['users', userId], // Уникальный ключ
queryFn: async () => {
const response = await fetch(`/api/v1/users/${userId}`);
return response.json();
}
});
}
// Если вызвать дважды с одинаковым userId
// Второй вызов вернет кэшированные данные без запроса
const user1 = useUser(123); // API запрос
const user2 = useUser(123); // Из кэша
Жизненный цикл кэша
const { status, data } = useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
staleTime: 5 * 60 * 1000, // 5 минут - данные "свежие"
gcTime: 10 * 60 * 1000 // 10 минут - данные в памяти
});
// Состояния:
// 1. fresh (свежие) - использу из кэша БЕЗ перезапроса
// 2. stale (устаревшие) - используй из кэша, но запроси обновление
// 3. invalid - нет данных, нужен запрос
// 4. gc (garbage collected) - выброшено из памяти
Визуальная шкала времени
Время -> 0мин ----5мин (staleTime)--- 10мин (gcTime)---- 15мин
| | | |
Статус fresh stale evicted gone
Кэш есть есть нет нет
Zapros нет фоновый да да
Стратегии кэширования
1. Кэш и синхронизация в фоне
// Данные из кэша сразу, но обновляется в фоне
function UserProfile({ userId }) {
const { data, isLoading, isRefetching } = useQuery({
queryKey: ['users', userId],
queryFn: fetchUser,
staleTime: 1 * 60 * 1000, // 1 минута
refetchOnWindowFocus: true // Обновить при фокусе окна
});
return (
<div>
{data && <h1>{data.name}</h1>}
{isRefetching && <span>Обновляется...</span>}
</div>
);
}
2. Долгоживущий кэш
// Данные кэшируются надолго, редко обновляются
function CountriesList() {
return useQuery({
queryKey: ['countries'],
queryFn: fetchCountries,
staleTime: 24 * 60 * 60 * 1000, // 24 часа
gcTime: 48 * 60 * 60 * 1000 // 48 часов
});
}
3. Всегда свежие данные
// Запрашиваем сервер при каждом использовании
function RealtimeNotifications() {
return useQuery({
queryKey: ['notifications'],
queryFn: fetchNotifications,
staleTime: 0, // Сразу считаются устаревшими
refetchInterval: 3000 // Запрос каждые 3 секунды
});
}
Инвалидация кэша (Invalidation)
Чтобы заставить перезапрос:
function CreatePost() {
const queryClient = useQueryClient();
const createMutation = useMutation({
mutationFn: createPost,
onSuccess: () => {
// После создания - инвалидируем кэш постов
queryClient.invalidateQueries({
queryKey: ['posts']
});
}
});
return (
<button onClick={() => createMutation.mutate({ title: 'New' })}>
Создать пост
</button>
);
}
Wildcards в Query Keys
const queryClient = useQueryClient();
// Инвалидировать все посты любого пользователя
await queryClient.invalidateQueries({
queryKey: ['users'] // Все запросы начинающиеся с 'users'
});
// Инвалидировать посты конкретного пользователя
await queryClient.invalidateQueries({
queryKey: ['users', 123, 'posts']
});
// Более точно с exact
await queryClient.invalidateQueries({
queryKey: ['users', 123],
exact: true // Только точное совпадение
});
Оптимистичные обновления
function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateUserOnServer,
onMutate: async (newData) => {
// Отмени текущие запросы
await queryClient.cancelQueries({ queryKey: ['users'] });
// Сохрани старые данные
const previousUser = queryClient.getQueryData(['users', newData.id]);
// Обнови кэш оптимистично
queryClient.setQueryData(['users', newData.id], newData);
return { previousUser }; // Возврат контекста
},
onError: (err, newData, context) => {
// При ошибке откати
if (context?.previousUser) {
queryClient.setQueryData(
['users', newData.id],
context.previousUser
);
}
},
onSuccess: () => {
// После успеха инвалидируй кэш
queryClient.invalidateQueries({ queryKey: ['users'] });
}
});
}
Отключение кэширования
// Каждый раз запрашиваем с сервера
function AlwaysFreshData() {
return useQuery({
queryKey: ['data'],
queryFn: fetchData,
staleTime: 0, // Никогда не кэшируй
gcTime: 0, // Не храни
enabled: true // Автозапрос
});
}
Синхронизация между вкладками
// React Query автоматически синхронизирует кэш
// когда переключаешься между вкладками
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: true // Обновить при фокусе
}
}
});
// Отработает когда вернешь фокус на вкладку
Отладка кэша
// Установи React Query DevTools
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
function App() {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
{/* app */}
</QueryClientProvider>
);
}
// Открой DevTools -> React Query
// Увидишь все ключи, их статус, размер кэша, времена
Практический пример: Список пользователей
function UsersList() {
const queryClient = useQueryClient();
const [page, setPage] = useState(1);
const { data, isLoading } = useQuery({
queryKey: ['users', { page }],
queryFn: () => fetchUsers(page),
staleTime: 5 * 60 * 1000,
keepPreviousData: true // Показать старые данные при load
});
const deleteMutation = useMutation({
mutationFn: deleteUser,
onSuccess: () => {
// Инвалидируй список
queryClient.invalidateQueries({ queryKey: ['users'] });
}
});
return (
<div>
{isLoading && <Spinner />}
{data?.users.map(user => (
<div key={user.id}>
{user.name}
<button onClick={() => deleteMutation.mutate(user.id)}>
Удалить
</button>
</div>
))}
</div>
);
}
Основные параметры кэша
- queryKey: Уникальный ключ для кэша
- staleTime: Как долго данные считаются свежими (0 = сразу устаревают)
- gcTime: Как долго неиспользуемые данные в памяти (ранее cacheTime)
- refetchOnWindowFocus: Обновить при фокусе окна
- refetchInterval: Интервал фонового обновления
- enabled: Включить/выключить автоматический запрос
- keepPreviousData: Показать старые данные во время загрузки
Выводы
React Query кэширует по ключам, умно управляет памятью, и предоставляет инструменты для инвалидации и синхронизации. Это экономит запросы и ускоряет приложение.