Что такое Redux Toolkit?
Перед вами самая лаконичная, компактная и современная библиотека на базе Redux! Redux Toolkit (RTK) – это официальный инструмент Redux, который значительно упрощает процесс разработки, минимизируя схватку с "боевыми роботами" (нет, не с багами, те проще!).
RTK создали, чтобы избавить разработчиков от излишне сложной конфигурации традиционного Redux (где нужно было вручную подключать кучу разных модулей, определять действия, редьюсеры и прочее). Здесь вы получите "всё включено" в одном пакете: настройку хранилища, работу с асинхронными экшенами и даже инструменты для иммутабельности.
Преимущества Redux Toolkit:
- Меньше кода: вы сокращаете количество шаблонного кода.
- Асинхронная работа: интегрированный инструмент
createAsyncThunkдля работы с API. - Типизация: RTK идеально подходит для TypeScript, делая ваш код безопасным.
- Иммутабельность: библиотека
immerвстроена прямо в Toolkit, позволяя вам менять состояние "как будто напрямую", но без нарушений правил Redux.
А теперь — поехали устанавливать и настраивать!
1. Установка необходимых пакетов
Мы начнём с установки RTK и реактовской библиотеки для работы с Redux. Как говорится, "никакой магии, только npm и немного терпения".
npm install @reduxjs/toolkit react-redux
Если вы работаете с Yarn (потому что вам нравится стиль команд в одном слове):
yarn add @reduxjs/toolkit react-redux
Что мы установили:
@reduxjs/toolkit: это сама библиотека Redux Toolkit.react-redux: официальная библиотека для интеграции Redux с React, позволяющая повесить глобальное хранилище на наше приложение и использовать хуки вродеuseSelectorиuseDispatch.
2. Настройка проекта: создаём Redux Store
Redux Store — это сердце Redux-приложения. Здесь мы будем хранить всё его состояние. Настроим его с помощью метода configureStore, который входит в Redux Toolkit.
Почему configureStore лучше, чем ручная настройка? Он автоматически подключает несколько полезных инструментов, таких как DevTools для отладки и встроенная защита от мутаций состояния. Никакой боли с настройкой middlewares!
Создаём файл для Store
Для начала создайте папку redux в корне проекта. Внутри создайте файл store.ts.
// redux/store.ts
import { configureStore } from '@reduxjs/toolkit';
export const store = configureStore({
reducer: {}, // Пока тут пусто, позже добавим редьюсеры
});
// Типизируем RootState и AppDispatch для TypeScript
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Обратите внимание:
- Мы пока передали пустой объект в
reducer— сюда позже подключаются наши срезы (slices), которые будут управлять различными частями состояния. - Мы определили типы
RootStateиAppDispatch. Это нужно, чтобы позже типизировать работу с хукамиuseSelectorиuseDispatch.
3. Оборачиваем <App /> в <Provider>
Чтобы React-компоненты могли получать доступ к Redux Store, нам нужно внедрить компонент <Provider> из библиотеки react-redux. Он позволяет передать Store через контекст, чтобы компоненты могли его использовать.
Откройте файл src/index.tsx или src/main.tsx (в зависимости от вашего проекта) и добавьте код:
// index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { store } from './redux/store'; // Подключаем наш Store
import App from './App';
ReactDOM.render(
<Provider store={store}> {/* Передаём Store в Provider */}
<App />
</Provider>,
document.getElementById('root')
);
Теперь наше приложение обёрнуто в <Provider>, а значит, любой компонент внутри <App /> сможет использовать Redux Store. Вам даже не нужно передавать данные через пропсы (да-да, время прощаться с "пропс-дриллингом").
4. Добавляем первый Slice
Redux Toolkit предлагает концепцию "срезов" (slice). Slice — это логическая часть состояния (например, данные о пользователе, список задач), а также набор методов для управления этим состоянием.
Создайте файл redux/slices/counterSlice.ts:
// redux/slices/counterSlice.ts
import { createSlice } from '@reduxjs/toolkit';
// Определяем интерфейс для состояния
interface CounterState {
value: number;
}
// Устанавливаем начальное состояние
const initialState: CounterState = {
value: 0,
};
// Создаём Slice
const counterSlice = createSlice({
name: 'counter', // Имя среза
initialState,
reducers: {
increment(state) {
state.value += 1; // Immer позволяет нам "казаться" будто мы мутируем состояние
},
decrement(state) {
state.value -= 1;
},
addByAmount(state, action) {
state.value += action.payload;
},
},
});
// Экспортируем экшены и редьюсер
export const { increment, decrement, addByAmount } = counterSlice.actions;
export default counterSlice.reducer;
Что здесь происходит:
createSlice: это метод, который создаёт срез. Он генерирует действия (increment,decrement,addByAmount) и редьюсер.- Immer: позволяет нам использовать мутационный синтаксис при работе с состоянием, сохраняя его неизменяемость под капотом. Подробнее через несколько лекций.
- Типизация: мы описали интерфейс
CounterState, чтобы TypeScript понял, что состояние должно быть числом.
5. Подключаем Slice к Store
Чтобы Store "узнал" о нашем новом срезе, мы должны добавить его в configureStore.
Откройте redux/store.ts и подключите counterSlice:
// redux/store.ts
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './slices/counterSlice'; // Импортируем редьюсер
export const store = configureStore({
reducer: {
counter: counterReducer, // Добавляем Slice в Store
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Теперь наш Slice встроен в глобальный Store. Данные из него будут доступны через путь state.counter.
6. Подключаем Slice к компоненту
Создайте базовый компонент Counter.tsx, который будет взаимодействовать с нашим срезом.
// components/Counter.tsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from '../redux/store'; // Импортируем RootState для типизации
import { increment, decrement, addByAmount } from '../redux/slices/counterSlice';
const Counter = () => {
const dispatch = useDispatch();
const counter = useSelector((state: RootState) => state.counter.value);
return (
<div>
<h1>Counter: {counter}</h1>
<button onClick={() => dispatch(increment())}>+1</button>
<button onClick={() => dispatch(decrement())}>-1</button>
<button onClick={() => dispatch(addByAmount(5))}>+5</button>
</div>
);
};
export default Counter;
В этом примере:
useSelectorизвлекает значение из Store.useDispatchотправляет экшены в Store.- Мы типизировали
stateвuseSelectorс помощьюRootState.
Теперь ваш проект готов к работе с Redux Toolkit, и вы можете наслаждаться управлением состоянием без лишней боли. Хотите добавить ещё один Slice? Легко! Теперь это дело пары минут.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ