Пришло время применить всё это на практике и создать приложение, которое делает запросы к API, использует Thunk для обработки данных, управляет состоянием загрузки и отображает результат пользователю.
Шаг 1: подготовка окружения
Для начала убедимся, что у нас установлен Redux и Redux Thunk. Если вы еще не установили их в своём проекте, выполните:
npm install @reduxjs/toolkit react-redux redux-thunk
Мы также будем работать с библиотекой axios для выполнения API-запросов:
npm install axios
Теперь у вас должно быть готовое окружение для дальнейших шагов.
Шаг 2: создание Redux Store и настройка Thunk
Redux Store — это "мозг" нашего приложения, который управляет состоянием. Добавим папку store и создадим файл store.ts:
// src/store/store.ts
import { configureStore } from '@reduxjs/toolkit';
import thunk from 'redux-thunk';
import { rootReducer } from './rootReducer';
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(thunk),
});
// Типизация для нашего Redux Store
export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export default store;
Эта конфигурация добавляет поддержку Redux Thunk в качестве middleware и создаёт базовый store.
Шаг 3: создание среза для управления данными
Теперь создадим срез для работы с данными. Мы будем использовать JSONPlaceholder API для получения списка пользователей. Добавляем файл userSlice.ts в папку store:
// src/store/userSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
interface User {
id: number;
name: string;
email: string;
}
interface UserState {
users: User[];
loading: boolean;
error: string | null;
}
const initialState: UserState = {
users: [],
loading: false,
error: null,
};
// Наш срез (slice) состояния пользователей
const userSlice = createSlice({
name: 'users',
initialState,
reducers: {
fetchUsersRequest(state) {
state.loading = true;
state.error = null;
},
fetchUsersSuccess(state, action: PayloadAction<User[]>) {
state.loading = false;
state.users = action.payload;
},
fetchUsersFailure(state, action: PayloadAction<string>) {
state.loading = false;
state.error = action.payload;
},
},
});
export const { fetchUsersRequest, fetchUsersSuccess, fetchUsersFailure } = userSlice.actions;
export default userSlice.reducer;
В этом срезе мы определили три действия:
fetchUsersRequest: устанавливает состояние загрузки.fetchUsersSuccess: записывает данные в состояние.fetchUsersFailure: сохраняет сообщение об ошибке.
Шаг 4: настройка редьюсера
Теперь добавим наш userSlice в корневой редьюсер rootReducer:
// src/store/rootReducer.ts
import { combineReducers } from 'redux';
import userReducer from './userSlice';
export const rootReducer = combineReducers({
users: userReducer,
});
Шаг 5: создание Thunk для API-запросов
Настало время создать Thunk для выполнения API-запроса. Добавляем файл userThunks.ts:
// src/store/userThunks.ts
import { AppDispatch } from './store';
import axios from 'axios';
import { fetchUsersRequest, fetchUsersSuccess, fetchUsersFailure } from './userSlice';
// Thunk для получения списка пользователей
export const fetchUsers = () => async (dispatch: AppDispatch) => {
dispatch(fetchUsersRequest());
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/users');
dispatch(fetchUsersSuccess(response.data));
} catch (error) {
dispatch(fetchUsersFailure('Не удалось загрузить пользователей.'));
}
};
Этот Thunk делает запрос к API, отправляет действия на основе состояния (загрузка, успех или ошибка) и передаёт данные в Redux.
Шаг 6: интеграция Redux в React
Добавим Redux в наше React-приложение. В файле index.tsx подключим Provider:
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import store from './store/store';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<Provider store={store}>
<App />
</Provider>
);
Теперь все компоненты внутри <Provider> имеют доступ к Redux.
Шаг 7: создание компонента для отображения пользователей
Создадим компонент UserList.tsx, который отображает данные, полученные из API:
// src/components/UserList.tsx
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUsers } from '../store/userThunks';
import { RootState } from '../store/store';
const UserList: React.FC = () => {
const dispatch = useDispatch();
const { users, loading, error } = useSelector((state: RootState) => state.users);
useEffect(() => {
dispatch(fetchUsers());
}, [dispatch]);
if (loading) return <p>Загрузка...</p>;
if (error) return <p>Ошибка: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name} ({user.email})
</li>
))}
</ul>
);
};
export default UserList;
Этот компонент:
- Использует
dispatchдля вызова Thunk при монтировании. - Использует
useSelectorдля доступа к состоянию из Redux. - Отображает данные, состояние загрузки или ошибки.
Шаг 8: добавление компонента в приложение
Теперь добавим наш компонент в App.tsx:
// src/App.tsx
import React from 'react';
import UserList from './components/UserList';
const App: React.FC = () => {
return (
<div>
<h1>Список пользователей</h1>
<UserList />
</div>
);
};
export default App;
Шаг 9: запуск приложения
Запустите приложение командой:
npm start
Откройте браузер: вы увидите список пользователей, загружаемый из API.
Особенности и типичные ошибки
При работе с Thunk часто возникает проблема с "зависаниями" состояния, когда забывается обработка ошибок. Другой момент — при использовании TypeScript может быть трудно сразу типизировать сложные данные. В таких случаях тщательно определяйте интерфейсы и используйте консоль для отладки.
Теперь вы понимаете, как использовать Redux Thunk для создания приложения с API-запросами. С этими знаниями вы сможете обрабатывать данные и работать с асинхронными процессами в более масштабных проектах!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ