JavaRush /Курсы /Модуль 3: React /Типизация состояния и действий в Redux с TypeScript

Типизация состояния и действий в Redux с TypeScript

Модуль 3: React
6 уровень , 3 лекция
Открыта

Типизация состояния (State)

Состояние в Redux — это сердце вашего приложения. Начнем с типизации самого состояния.

Шаг 1: Создание интерфейса для состояния

Создадим интерфейс, который описывает структуру нашего состояния. Допустим, мы работаем над приложением для управления задачами (todo list).

// types.ts
export interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

export interface TodoState {
  todos: Todo[];
}

Здесь TodoState представляет корневое состояние, в котором хранятся задачи в виде массива из объектов Todo.

Попробуйте представить интерфейсы как рамки, которые держат ваш код в рамках логики, а не хаоса.

Шаг 2: Использование типов в createSlice

Теперь мы подключим интерфейс состояния к нашему срезу.

// features/todoSlice.ts
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import { Todo, TodoState } from '../types';

// Начальное состояние
const initialState: TodoState = {
  todos: [],
};

const todoSlice = 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((t) => t.id === action.payload);
      if (todo) {
        todo.completed = !todo.completed;
      }
    },
  },
});

export const { addTodo, toggleTodo } = todoSlice.actions;
export default todoSlice.reducer;
  • initialState строго типизирован как TodoState.
  • PayloadAction<T> позволяет указать тип полезной нагрузки (payload), которую принимает каждое действие (action).

Теперь, когда вы вызовете addTodo или toggleTodo, TypeScript будет проверять, что вы передаете данные правильного типа.

Типизация действий (Actions)

Redux-редьюсер обрабатывает действия (actions), чтобы изменять состояние. Давайте разберемся, как типизировать эти действия.

Шаг 1: Типизация Payload в действиях

Мы используем PayloadAction из Redux Toolkit для типизации полезной нагрузки. Это позволяет нам четко определить структуру данных, передаваемых в действия.

Пример:

  • Действие addTodo ожидает объект типа Todo в качестве полезной нагрузки.
  • Действие toggleTodo работает с number — ID задачи.

Все это мы уже указали в коде выше. Но, если вы захотите использовать стандартный Action из Redux (без Toolkit), типизация выгладит примерно так:

interface AddTodoAction {
  type: 'ADD_TODO';
  payload: Todo;
}

interface ToggleTodoAction {
  type: 'TOGGLE_TODO';
  payload: number;
}

type TodoActions = AddTodoAction | ToggleTodoAction;

Toolkit избавляет нас от необходимости создавать такие интерфейсы вручную — работа становится проще и быстрее.

Шаг 2: Типизация асинхронных действий

Асинхронные действия async actions — это особая тема. Для их обработки в Redux Toolkit используется createAsyncThunk. Давайте разберемся, как типизировать такие действия.

Допустим, мы хотим загрузить список задач с сервера.

// features/todoSlice.ts
import { createAsyncThunk } from '@reduxjs/toolkit';
import { Todo } from '../types';

// Типизация: функция возвращает массив объектов Todo
export const fetchTodos = createAsyncThunk<Todo[], void>(
  'todos/fetchTodos',
  async () => {
    const response = await fetch('https://jsonplaceholder.typicode.com/todos');
    const data: Todo[] = await response.json();
    return data;
  }
);
  • Первый параметр типизирует данные, которые возвращает наша функция Todo[].
  • Второй параметр типизирует аргументы, передаваемые в нашу функцию (здесь это void, потому что аргументы не передаются).

Такой подход позволяет вам быть уверенными в корректности ваших данных на каждом этапе работы.

Пользы от типизации в Redux

Теперь, когда мы знаем, как типизировать состояние и действия, давайте посмотрим, какие конкретные преимущества это дает.

1. Предотвращение ошибок

TypeScript моментально подскажет, если вы попытаетесь передать в редьюсер или действие данные неверного типа.

Ошибка без типизации:

store.dispatch(addTodo({ id: 'oops', title: 'Invalid Todo', completed: false }));
// Упс! ID должен быть числом, но ошибка всплывет только во время выполнения.

Ошибка с типизацией:

// TypeScript сразу предупреждает:
store.dispatch(addTodo({ id: 'oops', title: 'Invalid Todo', completed: false }));

2. Улучшение автодополнения

TypeScript улучшает автодополнение в редакторах кода. Когда вы работаете с состоянием или действиями, редактор будет предлагать подсказки, что снижает вероятность ошибок и ускоряет разработку.

Пример:

const todo = useSelector((state: RootState) => state.todo.todos[0]);
// Редактор подскажет, что у объекта "todo" есть поля id, title и completed.

Практическое применение

Вы можете применять типизацию в Redux для достижения таких целей:

  • Разработка сложных приложений, где состояние включает в себя сложные структуры данных (например, мультиязычные приложения).
  • Упрощение работы в командах — строгая типизация помогает коллегам быстро разобраться в вашем коде.
  • Успешное прохождение собеседований — типизация в Redux часто обсуждается при найме React-разработчиков.
1
Задача
Модуль 3: React, 6 уровень, 3 лекция
Недоступна
Типизация состояния
Типизация состояния
1
Задача
Модуль 3: React, 6 уровень, 3 лекция
Недоступна
Типизированный срез (slice)
Типизированный срез (slice)
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ