RTK Query появился как развитие подхода к работе с серверным состоянием в экосистеме Redux Toolkit и во многих проектах стал заменой классическим асинхронным слоям на базе Redux Thunk. Миграция с Redux Thunk на RTK Query обычно затрагивает не только слой получения данных, но и архитектуру хранения, кэширования, инвалидации и управления состоянием загрузки.
Redux Thunk реализует асинхронную логику вручную через функции-диспетчеры. Типичный поток включает:
RTK Query заменяет этот подход декларативной моделью:
Ключевое различие заключается в том, что Redux Thunk управляет процессом, а RTK Query описывает данные и их получение.
Перед переходом важно разделить существующие thunk-логики на категории:
Именно эта категория является основной целью миграции в RTK Query.
Такая логика обычно остаётся в reducers или middleware.
Именно смешанные случаи требуют рефакторинга перед переходом.
Основной шаг миграции — создание API-сервиса через
createApi.
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
}),
});
На этом этапе важно определить базовый URL и единый механизм
запросов. В большинстве случаев fetchBaseQuery заменяет
axios-инстансы, используемые в thunk-слое.
Исходный thunk:
export const fetchUsers = () => async (dispatch) => {
dispatch(usersLoading());
try {
const response = await fetch('/api/users');
const data = await response.json();
dispatch(usersSuccess(data));
} catch (e) {
dispatch(usersError(e.toString()));
}
};
RTK Query эквивалент:
endpoints: (builder) => ({
getUsers: builder.query({
query: () => '/users',
}),
});
Ключевое изменение:
При миграции важно правильно подключить reducer и middleware:
import { configureStore } from '@reduxjs/toolkit';
import { api } from './api';
export const store = configureStore({
reducer: {
[api.reducerPath]: api.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(api.middleware),
});
Middleware RTK Query обеспечивает:
В thunk-подходе данные часто загружались через useEffect:
useEffect(() => {
dispatch(fetchUsers());
}, []);
В RTK Query это заменяется хуком:
const { data, error, isLoading } = useGetUsersQuery();
Изменяется сам принцип:
Thunk:
export const fetchUserById = (id) => async (dispatch) => {
const res = await fetch(`/api/users/${id}`);
return res.json();
};
RTK Query:
getUserById: builder.query({
query: (id) => `/users/${id}`,
});
Использование:
const { data } = useGetUserByIdQuery(userId);
Параметры запроса становятся частью ключа кэша автоматически.
В thunk-архитектуре обновление данных требует ручного повторного запроса:
RTK Query использует систему тегов:
getUsers: builder.query({
query: () => '/users',
providesTags: ['Users'],
});
addUser: builder.mutation({
query: (body) => ({
url: '/users',
method: 'POST',
body,
}),
invalidatesTags: ['Users'],
});
После добавления пользователя список обновляется автоматически.
Исходный thunk:
export const createUser = (user) => async (dispatch) => {
const res = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(user),
});
const data = await res.json();
dispatch(userCreated(data));
};
RTK Query:
addUser: builder.mutation({
query: (user) => ({
url: '/users',
method: 'POST',
body: user,
}),
});
Использование:
const [addUser] = useAddUserMutation();
После переноса endpoints становится возможным удалить:
Остаются только:
Redux Thunk часто использует:
RTK Query заменяет это кэшем:
Это меняет подход к проектированию store: серверное состояние больше не моделируется вручную.
В thunk архитектуре побочные эффекты часто выполнялись внутри async функций.
В RTK Query они реализуются через:
onQueryStartedonCacheEntryAddedПример:
getUsers: builder.query({
query: () => '/users',
async onQueryStarted(arg, { queryFulfilled }) {
try {
await queryFulfilled;
} catch (e) {
console.log('error');
}
},
});
Миграция не требует полного отказа от thunk сразу. Возможна гибридная архитектура:
Например:
Это приводит к дублированию данных и рассинхронизации состояния.
RTK Query не предназначен для сложных вычислений или orchestration логики.
Без providesTags и invalidatesTags теряется
основное преимущество автоматической актуализации данных.
Правильная миграция требует пересмотра архитектуры, а не механической замены.
RTK Query расширяет Redux DevTools:
Middleware больше не управляет асинхронностью вручную, а становится инфраструктурным слоем RTK Query.
Переход с Redux Thunk на RTK Query фактически меняет модель работы с данными:
Архитектура становится ориентированной на данные, а не на действия.