Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
UseQuery в RTK Query
RTK Query (Redux Toolkit Query) - это мощный инструмент для управления кешированием данных и синхронизацией состояния с сервером. useQuery - это основной хук для получения данных.
1. Что такое RTK Query
RTK Query - это встроенный инструмент Redux Toolkit, который предоставляет набор возможностей для упрощения загрузки, кеширования и синхронизации данных с сервером. Это альтернатива React Query с лучшей интеграцией с Redux.
// Установка
// npm install @reduxjs/toolkit react-redux
// Импорты
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
2. Создание API слайса
Первый шаг - определить API с помощью createApi:
// api.ts
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
interface User {
id: number;
name: string;
email: string;
}
export const userApi = createApi({
reducerPath: 'userApi',
baseQuery: fetchBaseQuery({
baseUrl: 'https://api.example.com/api/v1'
}),
endpoints: (builder) => ({
getUser: builder.query<User, number>({
query: (userId) => `/users/${userId}`
}),
getUsers: builder.query<User[], void>({
query: () => '/users'
}),
updateUser: builder.mutation<User, { id: number; data: Partial<User> }>({
query: ({ id, data }) => ({
url: `/users/${id}`,
method: 'PUT',
body: data
})
})
})
});
export const { useGetUserQuery, useGetUsersQuery, useUpdateUserMutation } = userApi;
3. Базовое использование useQuery
useQuery автоматически управляет состоянием загрузки, ошибок и кеша:
import { useGetUserQuery } from './api';
function UserProfile({ userId }: { userId: number }) {
// useQuery автоматически выполняет запрос
const { data, isLoading, error } = useGetUserQuery(userId);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>{data?.name}</h1>
<p>{data?.email}</p>
</div>
);
}
4. Как работает useQuery - внутреннее устройство
Когда вы используете useQuery, происходит следующее:
Шаг 1: Инициализация запроса
- useQuery генерирует уникальный ключ кеша на основе endpoint и параметров
- Проверяет, есть ли уже данные в кеше
Шаг 2: Проверка кеша
// RTK Query проверяет кеш
const cacheKey = 'getUser(5)'; // Построено из endpoint и параметров
const cachedData = cache.get(cacheKey);
if (cachedData && !isStale(cachedData)) {
return cachedData; // Возвращает кешированные данные
}
Шаг 3: Выполнение запроса
- Если данные отсутствуют или устарели, RTK Query делает HTTP запрос
- Обновляет состояние (isLoading = true)
Шаг 4: Обновление состояния
- После ответа данные сохраняются в кеш
- Компонент перерендеривается с новыми данными
5. Управление кешем
RTK Query имеет встроенное управление кешем с опциями:
function UsersList() {
// Опции управления кешем
const { data, isLoading, isFetching } = useGetUsersQuery(undefined, {
// Как долго данные считаются свежими (в секундах)
pollingInterval: 0, // 0 = отключен
// Переполучить данные при изменении фокуса окна
refetchOnFocus: true,
// Переполучить данные при переподключении к сети
refetchOnReconnect: true,
// Переполучить данные при монтировании компонента
refetchOnMountOrArgChange: 60, // 60 секунд
// Пропустить запрос
skip: false
});
return (
<div>
{isLoading && <p>Loading...</p>}
{isFetching && <p>Refreshing...</p>}
{data?.map((user) => <div key={user.id}>{user.name}</div>)}
</div>
);
}
6. Полное жизненное действие useQuery
function CompleteExample() {
const {
data, // Текущие данные из кеша
isLoading, // true при первом запросе
isFetching, // true когда запрос в процессе
isSuccess, // true если запрос успешно завершен
isError, // true если произошла ошибка
error, // Объект ошибки
refetch, // Функция для ручного переполучения
fulfilledTimeStamp // Временная метка последнего успешного запроса
} = useGetUsersQuery(undefined, {
pollingInterval: 30000, // Переполучать каждые 30 секунд
refetchOnFocus: true,
refetchOnReconnect: true,
refetchOnMountOrArgChange: true
});
const handleRefresh = () => {
refetch(); // Принудительно переполучить данные
};
return (
<div>
{isLoading && <p>First load...</p>}
{isFetching && !isLoading && <p>Updating...</p>}
{isSuccess && (
<>
<button onClick={handleRefresh}>Refresh</button>
<ul>
{data?.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
<small>Last updated: {new Date(fulfilledTimeStamp!).toLocaleTimeString()}</small>
</>
)}
{isError && <p>Error: {(error as any).message}</p>}
</div>
);
}
7. Условный запрос с skip
Иногда нужно выполнить запрос только при определенных условиях:
function ConditionalQuery({ userId }: { userId?: number }) {
// Запрос выполнится только если userId определен
const { data, isLoading } = useGetUserQuery(userId!, {
skip: !userId // Пропустить запрос если userId undefined
});
if (!userId) return <p>Select a user</p>;
if (isLoading) return <p>Loading...</p>;
return <div>{data?.name}</div>;
}
8. Обработка ошибок
function UserWithErrorHandling({ userId }: { userId: number }) {
const { data, isLoading, error } = useGetUserQuery(userId);
if (isLoading) return <div>Loading...</div>;
if (error) {
const errorMessage = 'status' in error
? `Error ${error.status}: ${JSON.stringify(error.data)}`
: error.message;
return <div className="error">Failed to load user: {errorMessage}</div>;
}
return <div>{data?.name}</div>;
}
9. Комбинирование нескольких запросов
function UserWithPosts({ userId }: { userId: number }) {
const { data: user, isLoading: userLoading } = useGetUserQuery(userId);
const { data: posts, isLoading: postsLoading } = useGetUserPostsQuery(userId);
const isLoading = userLoading || postsLoading;
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h1>{user?.name}</h1>
<ul>
{posts?.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</div>
);
}
10. Интеграция с store
// store.ts
import { configureStore } from '@reduxjs/toolkit';
import { userApi } from './api';
export const store = configureStore({
reducer: {
[userApi.reducerPath]: userApi.reducer
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(userApi.middleware)
});
// App.tsx
import { Provider } from 'react-redux';
function App() {
return (
<Provider store={store}>
<YourComponent />
</Provider>
);
}
Ключевые преимущества RTK Query
- Автоматический кеш - встроенное управление кешем
- Состояния загрузки - isLoading, isFetching, isSuccess, isError
- Переполучение данных - pollingInterval, refetchOnFocus, refetchOnReconnect
- Типизация - полная типизация с TypeScript
- Нормализация - встроенная нормализация кеша
- Optimistic Updates - обновление UI перед получением ответа сервера
- Синхронизация - автоматическая синхронизация данных между вкладками
Когда использовать RTK Query
- Вы уже используете Redux
- Нужна сложная логика кеширования
- Требуется TypeScript поддержка
- Нужна интеграция с Redux DevTools
Альтернативы
- React Query (TanStack Query) - более гибкий, без зависимости от Redux
- SWR - простой и лёгкий вариант
- Axios с простым состоянием - для небольших проектов