Зачем нам Redux?
Когда в вашем приложении появляется всё больше данных — транзакции, пользовательская информация, баланс — становится сложно эффективно управлять состоянием только с помощью useState. Именно тут приходит на помощь Redux Toolkit — современный способ централизованного управления состоянием в React.
Установка Redux и Redux Toolkit
Redux Toolkit значительно упрощает работу с Redux и избавляет от лишнего шаблонного кода.
Откройте терминал и выполните:
npm install @reduxjs/toolkit react-redux
Теперь у нас есть:
@reduxjs/toolkit— основной инструмент для создания Store и срезов (slices).react-redux— мост между Redux и React.
Создание Redux Store
Store — это то место, где хранится все состояние нашего приложения. Давайте создадим его.
Создайте файл index.ts в папке src/store:
Создадим наше глобальное хранилище. src/store/index.ts:
// src/store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import transactionsReducer from '../features/transactions/transactionsSlice';
const store = configureStore({
reducer: {
transactions: transactionsReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
export default store;
Срез состояния: транзакции
Сделаем отдельный модуль управления транзакциями (доходы и расходы).
src/features/transactions/transactionsSlice.ts:
// src/features/transactions/transactionsSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
export interface Transaction {
id: number;
type: 'income' | 'expense';
category: string;
amount: number;
note?: string;
date: string;
}
interface TransactionsState {
transactions: Transaction[];
}
const initialState: TransactionsState = {
transactions: [],
};
const transactionsSlice = createSlice({
name: 'transactions',
initialState,
reducers: {
addTransaction: (state, action: PayloadAction<Omit<Transaction, 'id'>>) => {
const newTransaction: Transaction = {
...action.payload,
id: Date.now(),
};
state.transactions.push(newTransaction);
},
deleteTransaction: (state, action: PayloadAction<number>) => {
state.transactions = state.transactions.filter(tx => tx.id !== action.payload);
},
},
});
export const { addTransaction, deleteTransaction } = transactionsSlice.actions;
export default transactionsSlice.reducer;
В этом коде мы:
- Определили состояние
TransactionsState, которое содержит массив транзакций. - Создали три действия:
addTransaction— добавляет новую финансовую транзакцию.deleteTransaction— удаляет финансовую транзакцию.
Подключение Redux к React
Redux не будет работать без связки с React. Здесь нам поможет библиотека react-redux.
Откройте src/index.tsx и оберните всё приложение в Provider:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import App from './App';
import store from './store';
const root = ReactDOM.createRoot(document.getElementById('root')!);
root.render(
<Provider store={store}>
<App />
</Provider>
);
Теперь всё приложение подключено к Redux!
Работа с транзакциями в компонентах
Просмотр транзакций: TransactionList
src/components/TransactionList.tsx:
import React from 'react';
import { useSelector } from 'react-redux';
import { RootState } from '../store';
const TransactionList: React.FC = () => {
const transactions = useSelector((state: RootState) => state.transactions.transactions);
return (
<ul>
{transactions.map(tx => (
<li key={tx.id}>
{tx.date} — <strong>{tx.category}</strong> — {tx.type === 'income' ? '+' : '-'}${tx.amount}
</li>
))}
</ul>
);
};
export default TransactionList;
Добавление транзакции: AddTransactionForm
src/components/AddTransactionForm.tsx:
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { addTransaction } from '../features/transactions/transactionsSlice';
import { AppDispatch } from '../store';
const AddTransactionForm: React.FC = () => {
const dispatch = useDispatch<AppDispatch>();
const [amount, setAmount] = useState('');
const [category, setCategory] = useState('');
const [type, setType] = useState<'income' | 'expense'>('expense');
const handleAdd = () => {
if (amount && category) {
dispatch(addTransaction({
type,
category,
amount: parseFloat(amount),
date: new Date().toISOString(),
}));
setAmount('');
setCategory('');
}
};
return (
<div>
<input value={amount} onChange={e => setAmount(e.target.value)} placeholder="Сумма" type="number" />
<input value={category} onChange={e => setCategory(e.target.value)} placeholder="Категория" />
<select value={type} onChange={e => setType(e.target.value as 'income' | 'expense')}>
<option value="expense">Расход</option>
<option value="income">Доход</option>
</select>
<button onClick={handleAdd}>Добавить</button>
</div>
);
};
export default AddTransactionForm;
Добавим их в App.tsx
import React from 'react';
import AddTransactionForm from './components/AddTransactionForm';
import TransactionList from './components/TransactionList';
const App: React.FC = () => {
return (
<div>
<h1>Учет расходов</h1>
<AddTransactionForm />
<TransactionList />
</div>
);
};
export default App;
Теперь вы можете добавлять и отображать транзакции — всё с помощью Redux!
Типичные ошибки и советы
- Ошибка: забыли передать редьюсер в Store. Убедитесь, что все ваши редьюсеры добавлены.
- Ошибка: неправильные типы в useSelector и useDispatch. Используйте
RootStateиAppDispatch, чтобы избежать путаницы с типами данных. - Совет: храните большие данные (например, списки транзакций) в Redux, а временные данные (например, статус фильтрации) лучше оставить в состоянии компонентов.
Теперь вы готовы покорять вершины Redux и организовывать глобальное состояние как настоящий профессионал!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ