JavaRush /Курсы /Модуль 3: React /Создание Store и срезов (slices) с Redux Toolkit

Создание Store и срезов (slices) с Redux Toolkit

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

Что такое slice в Redux Toolkit

Добро пожаловать на лекцию, посвящённую одному из самых эффективных способов управления состоянием в React-приложениях — Redux Toolkit! Сегодня мы поговорим о срезах (slices), чтобы понять, как они упрощают и структурируют наш код. Готовы? Погнали!

На самом деле, createSlice — это главный герой сегодняшней лекции и настоящая магия Redux Toolkit. Slice — это более элегантный способ описать логику изменения состояния и действия (актионы) в одном месте. Раньше мы объявляли экшены, редьюсеры, типы действий и связывали их вручную. Это было сложно, муторно и часто конфузило разработчиков. Но Redux Toolkit говорит: "Хватит!". Теперь всё находится в одном месте.

Преимущества использования slice:

  1. Меньше boilerplate-кода — нет необходимости громоздить несколько файлов для экшенов и редьюсеров.
  2. Инкапсуляция логики — все состояния и действия описаны в одном месте.
  3. Лучший DX (Developer Experience) — меньше боли, больше удовольствия.

Slice можно представить как небольшую коробочку с "темой" вашего приложения. Например, у нас есть приложение для списка задач (todo-list). Мы можем создать todoSlice, который будет управлять состоянием задач: их добавлением, удалением и обновлением. Другой пример — authSlice для управления состоянием аутентификации, например, логином и токенами.

Создание первого slice

Давайте сразу перейдём к практике. Мы настроим slice для простого Todo-приложения. В этом приложении можно будет добавлять задачи, удалять их и изменять статус выполнения.

Начнём с установки Redux Toolkit. Если вы ещё не установили Redux Toolkit, запустите следующую команду:

npm install @reduxjs/toolkit react-redux

Эта команда добавляет Redux Toolkit и react-redux, чтобы связать Redux с React.

Создание файла todoSlice.ts

Сначала создадим файл, где и будет описана логика нашего Todo-приложения. Пусть он лежит в папке store/slices. Структура проекта может выглядеть так:

src/
  store/
    slices/
      todoSlice.ts

Теперь напишем наш todoSlice. Вот что нужно сделать.

import { createSlice, PayloadAction } from "@reduxjs/toolkit";

// Определяем интерфейс для состояния задач
interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

// Тип для начального состояния
interface TodoState {
  todos: Todo[];
}

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

// Создаём slice
const todoSlice = createSlice({
  name: "todos", // Название slice
  initialState, // Начальное состояние
  reducers: {
    // Добавляем задачу
    addTodo: (state, action: PayloadAction<string>) => {
      const newTodo: Todo = {
        id: crypto.randomUUID(),
        text: action.payload,
        completed: false,
      };
      state.todos.push(newTodo);
    },
    // Удаляем задачу
    removeTodo: (state, action: PayloadAction<string>) => {
      state.todos = state.todos.filter((todo) => todo.id !== action.payload);
    },
    // Переключение статуса задачи
    toggleTodo: (state, action: PayloadAction<string>) => {
      const todo = state.todos.find((todo) => todo.id === action.payload);
      if (todo) {
        todo.completed = !todo.completed;
      }
    },
  },
});

// Экспортируем действия
export const { addTodo, removeTodo, toggleTodo } = todoSlice.actions;

// Экспортируем редьюсер для добавления в Store
export default todoSlice.reducer;

Пояснения по коду

  • createSlice:

    • name: название среза, используется для определения пространства имён.
    • initialState: начальное состояние для среза.
    • reducers: это ваши редьюсеры. Они описывают, как изменяется состояние при выполнении определённых действий.
  • Типизация:

    • Мы используем PayloadAction<T> для определения типа данных, передаваемых в экшен.
  • Редьюсеры addTodo, removeTodo, toggleTodo:

    • Они модифицируют массив задач в зависимости от действия. Обратите внимание, что в Redux Toolkit можно мутировать state благодаря использованию Immer.

Интеграция с Store

Теперь, когда slice готов, нам нужно подключить его к Redux Store, чтобы с ним можно было работать из компонентов.

Файл store.ts

Создайте файл store.ts в папке store. В нём мы будем объединять все срезы в едином Store.

import { configureStore } from "@reduxjs/toolkit";
import todosReducer from "./slices/todoSlice";

export const store = configureStore({
  reducer: {
    todos: todosReducer, // Подключаем наш slice
  },
});

// Типизация для хуков useSelector и useDispatch
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

Связываем Store с React

Теперь нужно подключить наш Store к React-приложению. Для этого в файле index.tsx обернём наше приложение в Provider.

import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { store } from "./store";
import App from "./App";

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

Работа с Redux в React-компонентах

Теперь пришло время проверить работу нашего среза на практике.

Пример компонента для отображения и управления задачами

import React, { useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import { RootState, AppDispatch } from "../store";
import { addTodo, removeTodo, toggleTodo } from "../store/slices/todoSlice";

const TodoApp: React.FC = () => {
  const [task, setTask] = useState("");
  const todos = useSelector((state: RootState) => state.todos.todos);
  const dispatch: AppDispatch = useDispatch();

  const handleAddTask = () => {
    if (task.trim() !== "") {
      dispatch(addTodo(task));
      setTask("");
    }
  };

  return (
    <div>
      <h1vTodo List</h1>
      <input
        type="text"
        value={task}
        onChange={(e) => setTask(e.target.value)}
        placeholder="Enter a task"
      />
      <button onClick={handleAddTask}>Add Task</button>
      <ul>
        {todos.map((todo) => (
          <li key={todo.id}>
            <span
              style={{ textDecoration: todo.completed ? "line-through" : "none" }}
              onClick={() => dispatch(toggleTodo(todo.id))}
            >
              {todo.text}
            </span>
            <button onClick={() => dispatch(removeTodo(todo.id))}>Delete</button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoApp;
1
Задача
Модуль 3: React, 6 уровень, 2 лекция
Недоступна
Создание базового slice
Создание базового slice
1
Задача
Модуль 3: React, 6 уровень, 2 лекция
Недоступна
Интеграция slice в Store
Интеграция slice в Store
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ