Налаштування архітектури 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 тижні. Вартість — індивідуально.







