Что такое действия (actions)?
Action — это "вещь", которая говорит Redux, что именно вы хотите изменить в состоянии. Action — обычный объект, содержащий два обязательных поля:
- type — строка, уникально описывающая действие.
- payload (опционально) — данные, передаваемые вместе с действием.
Пример типичного Action:
{
type: 'todos/addTodo',
payload: {
id: 1,
title: 'Изучить Redux',
completed: false
}
}
Можно представить это как "приказ" для Redux: "Эй, добавь новую задачу в список!"
Что такое редьюсеры (reducers)?
Reducer — это чистая функция, которая отвечает за обновление состояния, основываясь на переданном действии. Представьте, что это маленькая фабрика обновлений: она берет текущее состояние и действие, а затем возвращает новое состояние.
Вот пример простого редьюсера:
const initialState = { count: 0 };
const counterReducer = (state = initialState, action: { type: string }) => {
switch (action.type) {
case 'counter/increment':
return { ...state, count: state.count + 1 };
case 'counter/decrement':
return { ...state, count: state.count - 1 };
default:
return state;
}
};
Создание действий и редьюсеров с помощью Redux Toolkit
Redux Toolkit значительно упрощает процесс создания действий и редьюсеров. Для этого используется функция createSlice, которая комбинирует действия и редьюсеры в одном месте.
Давайте создадим пример функциональности для списка задач.
1. Настройка Slice
Создадим Slice для управления задачами. Начнем с определения начального состояния:
interface Todo {
id: number;
title: string;
completed: boolean;
}
interface TodoState {
todos: Todo[];
}
const initialState: TodoState = {
todos: []
};
Теперь создаем Slice:
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
const todosSlice = createSlice({
name: 'todos',
initialState,
reducers: {
addTodo: (state, action: PayloadAction<Todo>) => {
state.todos.push(action.payload);
},
toggleTodo: (state, action: PayloadAction<number>) => {
const todo = state.todos.find((todo) => todo.id === action.payload);
if (todo) {
todo.completed = !todo.completed;
}
},
removeTodo: (state, action: PayloadAction<number>) => {
state.todos = state.todos.filter((todo) => todo.id !== action.payload);
}
}
});
Что здесь происходит?
- name: уникальное имя Slice.
- initialState: начальное состояние.
- reducers: набор функций, которые обновляют состояние. Каждая функция автоматически создает соответствующее действие.
2. Генерация действий
С помощью createSlice действия создаются автоматически. Вы можете получить их через поле actions:
export const { addTodo, toggleTodo, removeTodo } = todosSlice.actions;
Теперь вы можете отправлять эти действия из компонентов:
dispatch(addTodo({
id: 1,
title: 'Изучить Redux Toolkit',
completed: false
}));
dispatch(toggleTodo(1)); // Переключить состояние задачи с id = 1
dispatch(removeTodo(1)); // Удалить задачу с id = 1
3. Подключение редьюсера к Store
Не забудьте добавить редьюсер вашего Slice в Store:
import { configureStore } from '@reduxjs/toolkit';
import todosReducer from './todosSlice';
const store = configureStore({
reducer: {
todos: todosReducer
}
});
Особенности и типизация
Благодаря TypeScript и функциям Redux Toolkit, типизация действий становится почти "бесплатной". Все действия, созданные с помощью createSlice, автоматически имеют строгую типизацию.
Например, addTodo имеет следующий тип:
(type: 'todos/addTodo', payload: Todo)
Так же работает для других действий.
Практическое задание: расширяем функциональность
Попробуйте самостоятельно добавить возможность редактирования задачи. Вот подсказка:
- Создайте новый редьюсер
editTodo, который будет приниматьidзадачи и новыйtitleкакpayload. - Реализуйте логику редактирования задачи в массиве
todos.
Код для подсказки:
editTodo: (state, action: PayloadAction<{ id: number; title: string }>) => {
const todo = state.todos.find((todo) => todo.id === action.payload.id);
if (todo) {
todo.title = action.payload.title;
}
}
Типичные ошибки (и как их избегать)
Изменение состояния напрямую: Redux требует, чтобы состояние было неизменяемым. Но функции в
createSliceиспользуютImmerпод капотом, который позволяет изменять состояния так, будто они изменяемые. Если вы не используете Redux Toolkit — не забудьте оObject.assignили операторах развертки....Поломка типов: следите за строгой типизацией
PayloadAction. Не оставляйтеpayloadбез типа, иначе TypeScript может сильно расстроиться.Дублирование логики: действия и редьюсеры должны быть короткими и независимыми. Не пытайтесь затолкать весь бизнес-слой в редьюсер!
Реальное применение
Итак, зачем все это нужно? Разработка на Redux Toolkit полезна в реальных проектах, особенно когда вы имеете дело со сложным состоянием, например:
- Взаимодействие с API для загрузки данных.
- Управление формами с несколькими уровнями валидации.
- Построение масштабируемых приложений с несколькими независимыми подмодулями.
Теперь, после освоения этой темы, вы можете смело использовать Redux Toolkit для любых сложных задач управления состоянием.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ