Настройка архитектуры Redux для React Native-приложения
Redux в React Native — классика. До появления хуков и современных альтернатив это был единственный способ надёжно управлять сложным состоянием. Сейчас Redux Toolkit (RTK) убрал главный аргумент против: бойлерплейт. С createSlice, createAsyncThunk и RTK Query код стал вдвое компактнее, а типобезопасность через TypeScript — полной.
Redux Toolkit: современный Redux
// store/slices/profileSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import { userApi } from '../api/userApi';
export const fetchProfile = createAsyncThunk(
'profile/fetch',
async (userId: string, { rejectWithValue }) => {
try {
return await userApi.getProfile(userId);
} catch (e) {
return rejectWithValue((e as Error).message);
}
}
);
interface ProfileState {
data: UserProfile | null;
loading: boolean;
error: string | null;
}
const profileSlice = createSlice({
name: 'profile',
initialState: { data: null, loading: false, error: null } as ProfileState,
reducers: {
clearProfile: (state) => { state.data = null; },
},
extraReducers: (builder) => {
builder
.addCase(fetchProfile.pending, (state) => { state.loading = true; state.error = null; })
.addCase(fetchProfile.fulfilled, (state, action) => {
state.loading = false;
state.data = action.payload;
})
.addCase(fetchProfile.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string;
});
},
});
Immer встроен в RTK: мутации внутри createSlice безопасны, иммутабельность гарантирована под капотом.
RTK Query для серверного состояния
Для API-запросов с кешированием RTK Query — лучший выбор в экосистеме RTK:
export const userApi = createApi({
reducerPath: 'userApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
endpoints: (builder) => ({
getProfile: builder.query<UserProfile, string>({
query: (userId) => `/users/${userId}`,
providesTags: (result, error, id) => [{ type: 'User', id }],
}),
updateProfile: builder.mutation<UserProfile, Partial<UserProfile>>({
query: (body) => ({ url: `/users/${body.id}`, method: 'PUT', body }),
invalidatesTags: (result, error, arg) => [{ type: 'User', id: arg.id }],
}),
}),
});
export const { useGetProfileQuery, useUpdateProfileMutation } = userApi;
В компоненте: const { data, isLoading, error } = useGetProfileQuery(userId). Кеш, дедупликация запросов, инвалидация — всё из коробки.
Middleware и Redux Saga
Для сложных асинхронных сценариев (цепочки запросов, WebSocket, отмена запросов) createAsyncThunk бывает недостаточно. Redux-Saga предоставляет генераторный подход:
function* fetchProfileSaga(action: ReturnType<typeof fetchProfile>) {
try {
const profile: UserProfile = yield call(userApi.getProfile, action.payload);
yield put(profileLoaded(profile));
} catch (e) {
yield put(profileFailed((e as Error).message));
}
}
Саги удобны для: параллельных запросов (all), отмены (race + cancel), повторных попыток (retry). Для большинства проектов createAsyncThunk достаточен.
Типизация store
export const store = configureStore({
reducer: {
profile: profileSlice.reducer,
[userApi.reducerPath]: userApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(userApi.middleware),
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// Типизированные хуки
export const useAppSelector = useSelector.withTypes<RootState>();
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
Что настраиваем
Настройка configureStore с RTK. Структура слайсов по фичам. RTK Query для API-слоя. Типизированные хуки. Настройка redux-persist для персистентности (если нужна). Базовые тесты слайсов через Jest.
Сроки
Настройка RTK + RTK Query с нуля: 2–3 дня. Миграция с legacy Redux (actions/reducers/thunks) на RTK: 1–2 недели. Стоимость — индивидуально.







