В чем разница между RTK и RTK Query?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
RTK vs RTK Query
RTK (Redux Toolkit) и RTK Query — это две части экосистемы управления состоянием Redux, но они решают разные задачи. RTK — это инструмент для управления состоянием приложения, а RTK Query — специализирована для работы с серверными данными и кешированием.
Redux Toolkit (RTK)
RTK — это официальный инструмент для упрощения работы с Redux. Он скрывает сложность Redux под удобным API и включает популярные middleware вроде Redux Thunk.
// Создание слайса (reducer + actions)
import { createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
name: 'user',
initialState: { data: null, loading: false, error: null },
reducers: {
setUser: (state, action) => {
state.data = action.payload;
},
clearUser: (state) => {
state.data = null;
}
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true;
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.data = action.payload;
state.loading = false;
})
.addCase(fetchUser.rejected, (state, action) => {
state.error = action.payload;
state.loading = false;
});
}
});
// Async thunk для асинхронных операций
import { createAsyncThunk } from '@reduxjs/toolkit';
const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId: number) => {
const response = await fetch(`/api/users/${userId}`);
return response.json();
}
);
RTK Query
RTK Query — это встроенная в Redux Toolkit библиотека для работы с серверными данными. Она автоматически управляет кешированием, синхронизацией и жизненным циклом данных.
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
const userApi = createApi({
reducerPath: 'userApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getUserById: builder.query({
query: (userId) => `/users/${userId}`,
// Автоматическое кеширование, синхронизация, etc.
}),
updateUser: builder.mutation({
query: ({ userId, ...patch }) => ({
url: `/users/${userId}`,
method: 'PATCH',
body: patch,
}),
// Автоматическое инвалидирование кеша
}),
}),
});
// Использование в компонентах
const { data: user, isLoading, error } = userApi.useGetUserByIdQuery(123);
const [updateUser] = userApi.useUpdateUserMutation();
Сравнение основных различий
| Аспект | RTK | RTK Query |
|---|---|---|
| Назначение | Управление состоянием приложения | Работа с серверными данными |
| Кеширование | Ручное управление | Автоматическое |
| Синхронизация | Ручная | Автоматическая |
| API запросы | Нужен createAsyncThunk | Встроено в createApi |
| Код | Больше boilerplate | Очень компактно |
| Сложность | Средняя | Низкая (для серверных данных) |
Подробное сравнение на примере
RTK подход (много кода):
const postSlice = createSlice({
name: 'posts',
initialState: { items: [], loading: false, error: null },
reducers: {
setLoading: (state, action) => {
state.loading = action.payload;
},
setItems: (state, action) => {
state.items = action.payload;
},
setError: (state, action) => {
state.error = action.payload;
},
addItem: (state, action) => {
state.items.push(action.payload);
},
updateItem: (state, action) => {
const index = state.items.findIndex(p => p.id === action.payload.id);
if (index !== -1) state.items[index] = action.payload;
},
deleteItem: (state, action) => {
state.items = state.items.filter(p => p.id !== action.payload);
}
}
});
const fetchPosts = createAsyncThunk(
'posts/fetchPosts',
async () => {
const response = await fetch('/api/posts');
return response.json();
}
);
const createPost = createAsyncThunk(
'posts/createPost',
async (post) => {
const response = await fetch('/api/posts', {
method: 'POST',
body: JSON.stringify(post)
});
return response.json();
}
);
// Много extraReducers для обработки...
RTK Query подход (компактно):
const postsApi = createApi({
reducerPath: 'postsApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getPosts: builder.query({
query: () => '/posts',
}),
createPost: builder.mutation({
query: (post) => ({
url: '/posts',
method: 'POST',
body: post,
}),
invalidatesTags: ['Post'], // Автоматическое инвалидирование
}),
updatePost: builder.mutation({
query: ({ id, ...patch }) => ({
url: `/posts/${id}`,
method: 'PATCH',
body: patch,
}),
invalidatesTags: ['Post'],
}),
deletePost: builder.mutation({
query: (id) => ({
url: `/posts/${id}`,
method: 'DELETE',
}),
invalidatesTags: ['Post'],
}),
}),
});
Использование в компонентах
RTK:
import { useDispatch, useSelector } from 'react-redux';
function PostsList() {
const dispatch = useDispatch();
const { items, loading } = useSelector(state => state.posts);
useEffect(() => {
dispatch(fetchPosts());
}, []);
return loading ? <Loader /> : <div>{items.map(p => <Post key={p.id} post={p} />)}</div>;
}
RTK Query:
function PostsList() {
const { data: items, isLoading } = postsApi.useGetPostsQuery();
return isLoading ? <Loader /> : <div>{items?.map(p => <Post key={p.id} post={p} />)}</div>;
}
Когда использовать что
Используй RTK для:
- Локального состояния приложения (UI состояния, фильтры, модали)
- Сложной бизнес-логики
- Состояния, которое не приходит с сервера
- Синхронизации между несколькими компонентами
Используй RTK Query для:
- Работы с REST API
- Кеширования серверных данных
- CRUD операций
- Синхронизации данных с сервером
- Оптимистичных обновлений
Комбинированный подход
const store = configureStore({
reducer: {
// Локальное состояние
ui: uiSlice.reducer,
filters: filtersSlice.reducer,
// Серверные данные
[postsApi.reducerPath]: postsApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(postsApi.middleware),
});
Вывод
RTK Query является частью Redux Toolkit и специализирована для серверных данных, RTK — для общего управления состоянием. В современных приложениях обычно используют оба: RTK Query для работы с API и RTK для локального UI состояния. Это позволяет избежать boilerplate и получить мощное кеширование автоматически.