React Query — это библиотека для управления асинхронными данными в приложениях на React. Она позволяет работать с серверными данными так же легко, как с локальным состоянием, автоматически кэшируя запросы, обновляя их при изменении данных и обеспечивая оптимистичное обновление интерфейса. Основные концепции включают кэширование, инвалидацию кэша, фоновые обновления и мутирование данных.
Для начала работы с React Query необходимо установить пакеты:
npm install @tanstack/react-query
Инициализация происходит через QueryClient и обертку QueryClientProvider, которая должна окружать всё приложение:
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<YourComponents />
</QueryClientProvider>
);
}
QueryClient отвечает за хранение всех запросов, кэшей и настроек по умолчанию.
Хук useQuery используется для получения данных. Основной
синтаксис:
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';
function Todos() {
const { data, isLoading, error } = useQuery(['todos'], async () => {
const response = await axios.get('/api/todos');
return response.data;
});
if (isLoading) return <div>Загрузка...</div>;
if (error) return <div>Ошибка загрузки</div>;
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
Ключевые моменты:
isLoading, error и data —
основные состояния запроса.Дополнительно можно использовать опции, например:
staleTime, cacheTime,
refetchOnWindowFocus.
Иногда нужно обновить данные после мутации. Для этого используется QueryClient.invalidateQueries:
import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
function AddTodo() {
const queryClient = useQueryClient();
const mutation = useMutation(newTodo => axios.post('/api/todos', newTodo), {
onSuccess: () => {
queryClient.invalidateQueries(['todos']);
}
});
const handleAdd = () => {
mutation.mutate({ title: 'Новая задача' });
};
return <button onCl ick={handleAdd}>Добавить</button>;
}
После успешной мутации React Query помечает кэшированные данные с
ключом 'todos' устаревшими и повторно их подтягивает.
Хук useMutation применяют для операций изменения данных.
Основные принципы:
onSuccess, onError,
onSettled для управления состоянием после выполнения
запроса.Пример оптимистичного обновления:
const queryClient = useQueryClient();
const mutation = useMutation(newTodo => axios.post('/api/todos', newTodo), {
onMutate: async newTodo => {
await queryClient.cancelQueries(['todos']);
const previousTodos = queryClient.getQueryData(['todos']);
queryClient.setQueryData(['todos'], old => [...old, newTodo]);
return { previousTodos };
},
onError: (err, newTodo, context) => {
queryClient.setQueryData(['todos'], context.previousTodos);
},
onSettled: () => {
queryClient.invalidateQueries(['todos']);
},
});
QueryClient позволяет задавать глобальные настройки:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60, // 1 минута
cacheTime: 1000 * 60 * 5, // 5 минут
refetchOnWindowFocus: true,
},
mutations: {
retry: 1,
}
}
});
staleTime — время, когда данные считаются свежими.cacheTime — время хранения данных в кэше после
устаревания.refetchOnWindowFocus — автоматический повторный запрос
при возврате на вкладку.React Query поддерживает выполнение нескольких запросов одновременно:
const todosQuery = useQuery(['todos'], fetchTodos);
const usersQuery = useQuery(['users'], fetchUsers);
if (todosQuery.isLoading || usersQuery.isLoading) return <div>Загрузка...</div>;
Для зависимых запросов можно использовать опцию
enabled:
const userQuery = useQuery(['user', userId], fetchUser, { enabled: !!userId });
Запрос выполняется только если userId существует.
React Query позволяет использовать select для
трансформации данных до передачи их в компонент:
const { data } = useQuery(['todos'], fetchTodos, {
select: todos => todos.filter(todo => !todo.completed),
});
Это позволяет избегать лишней фильтрации в компоненте и оптимизирует рендер.
React Query отлично сочетается с Chakra UI:
Spinner,
Alert.Modal для
подтверждения мутаций.Button,
FormControl для взаимодействия с мутациями.Пример списка задач с Chakra UI:
import { Box, Spinner, Text, List, ListItem } from '@chakra-ui/react';
function Todos() {
const { data, isLoading, error } = useQuery(['todos'], fetchTodos);
if (isLoading) return <Spinner />;
if (error) return <Text color="red.500">Ошибка загрузки</Text>;
return (
<List spacing={3}>
{data.map(todo => (
<ListItem key={todo.id}>
<Box p={2} borderWidth={1} borderRadius="md">
{todo.title}
</Box>
</ListItem>
))}
</List>
);
}
Использование React Query вместе с Chakra UI позволяет создавать интерфейсы, которые динамически обновляются, плавно отображают состояния загрузки и интуитивно реагируют на ошибки.
useQueryClient() — доступ к клиенту для инвалидации
кэша и управления состоянием.useInfiniteQuery() — для пагинации и бесконечных
списков.useIsFetching() — количество активных запросов, полезно
для глобальных индикаторов загрузки.Hydrate — для серверного рендеринга и предзагрузки
кэша.Эта система позволяет строить производительные, отзывчивые и устойчивые к ошибкам приложения, где данные остаются актуальными, а пользовательский интерфейс — плавным и предсказуемым.