Здравствуйте, товарищи разработчики! 🤘 Меня зовут Омкар, я фронтенд-разработчик, и я очень рад представить вам еще одну захватывающую часть моей серии React Hook. 🔥 В этом выпуске мы собираемся исследовать удивительный мир нестандартных крючков! Эти хуки меняют правила игры, позволяя нам писать многоразовый, модульный и эффективный код. 💪 Но прежде чем мы начнем, обязательно ознакомьтесь с предыдущими частями серии. Готовы ли вы вывести свои навыки React на новый уровень? Давайте погрузимся и зажжем это! 💻🚀

Хук useState в React — это мощный инструмент, упрощающий управление состоянием в функциональных компонентах. Однако по мере роста сложности управления состоянием использование useState может стать затруднительным и привести к дублированию кода. В таких случаях хук useReducer обеспечивает более масштабируемое и организованное решение. В этом посте мы рассмотрим хук useReducer в React, его преимущества и способы его использования.

Введение в использование редуктора

Хук useReducer — это встроенный хук React, который позволяет вам управлять состоянием более предсказуемым и масштабируемым способом. Он следует принципам шаблона редуктора, обычно используемого в Redux и других библиотеках управления состоянием. useReducer принимает функцию редуктора и начальное состояние и возвращает текущее состояние и функцию диспетчеризации для обновления состояния.

Хук useReducer — это способ управления состоянием в React путем отправки действий в функцию-редюсер. Он принимает два аргумента: функцию-редуктор и значение начального состояния. Функция редуктора определяет, как состояние изменяется в ответ на отправленные действия. Действия передаются как объекты со свойством type, указывающим тип действия, и необязательным свойством payload, содержащим дополнительные данные.

Синтаксис:

const [state, dispatch] = useReducer(reducer, initialState);

Давайте начнем с простого примера счетчика с помощью useReducer:

import React, { useReducer } from 'react';

const initialState = 0;

const reducer = (state, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
};

const Counter = () => {
  const [count, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>Decrement</button>
    </div>
  );
};

export default Counter;

Вывод:

Импорт зависимостей:

  • Мы импортируем хук useReducer из пакета react. Этот хук позволяет нам управлять состоянием внутри функционального компонента.

Исходное состояние и редьюсер:

  • Мы устанавливаем initialState в 0, представляя начальное значение нашего счетчика.
  • Функция reducer принимает текущее состояние и объект действия и в зависимости от типа действия выполняет соответствующее обновление состояния.
  • В этом случае, если тип действия 'INCREMENT', он увеличивает состояние на 1.
  • Если тип действия 'DECREMENT', состояние уменьшается на 1.
  • Если ни один из указанных выше типов действий не соответствует, он возвращает текущее состояние.

Компонент счетчика:

  • Мы определяем компонент Counter, который будет отображать значение счетчика и обеспечивать функциональность для увеличения и уменьшения счетчика.
  • Внутри компонента мы инициализируем состояние с помощью хука useReducer. Состояние управляется функцией reducer и начинается с initialState.
  • Хук useReducer возвращает массив с двумя элементами: текущее состояние (count) и функцию dispatch. Функция dispatch используется для отправки действий редюсеру.

Визуализация счетчика:

  • Внутри оператора return мы отображаем текущее значение счетчика, используя состояние count.
  • Мы отображаем значение счетчика в элементе <h2>.
  • Есть две кнопки: «Увеличить» и «Уменьшить».
  • Нажатие кнопки «Увеличить» запускает функцию dispatch с объектом действия типа 'INCREMENT'. Это увеличивает счет.
  • Нажатие кнопки «Уменьшить» запускает функцию dispatch с объектом действия типа 'DECREMENT'. Это уменьшает счет.

Теперь давайте рассмотрим более сложный пример списка задач с использованием useReducer:

import React, { useReducer, useState } from 'react';

const initialState = [];
const reducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [...state, { id: Date.now(), text: action.payload, completed: false }];
    case 'TOGGLE_TODO':
      return state.map(todo =>
        todo.id === action.payload ? { ...todo, completed: !todo.completed } : todo
      );
    case 'REMOVE_TODO':
      return state.filter(todo => todo.id !== action.payload);
    default:
      return state;
  }
};

const TodoList = () => {
  const [todos, dispatch] = useReducer(reducer, initialState);
  const [input, setInput] = useState('');

  const handleAddTodo = () => {
    if (input.trim() !== '') {
      dispatch({ type: 'ADD_TODO', payload: input });
      setInput('');
    }
  };

  return (
    <div>
      <input type="text" value={input} onChange={e => setInput(e.target.value)} />
      <button onClick={handleAddTodo}>Add Todo</button>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <span
              style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
              onClick={() => dispatch({ type: 'TOGGLE_TODO', payload: todo.id })}
            >
              {todo.text}
            </span>
            <button onClick={() => dispatch({ type: 'REMOVE_TODO', payload: todo.id })}>
              Remove
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default TodoList;

Вывод:

Давайте пройдемся по коду шаг за шагом:

Импорт зависимостей:

  • Мы импортируем хуки useReducer и useState из пакета react. Эти хуки позволяют нам управлять состоянием внутри функционального компонента.

Исходное состояние и редьюсер:

  • Мы определяем initialState как пустой массив, представляющий начальное состояние нашего списка задач.
  • Функция reducer принимает текущее состояние и объект действия и в зависимости от типа действия выполняет соответствующее обновление состояния.
  • В этом случае, если тип действия — 'ADD_TODO', он добавляет новый объект todo в массив состояния, включая идентификатор, текст и состояние завершения.
  • Если тип действия — 'TOGGLE_TODO', он переключает завершенный статус задачи, сопоставляя массив состояний.
  • Если тип действия — 'REMOVE_TODO', он отфильтровывает задачу с указанным идентификатором из массива состояний.
  • Если ни один из указанных выше типов действий не соответствует, он возвращает текущее состояние.

Компонент TodoList:

  • Мы определяем компонент TodoList, который будет отображать список задач и предоставлять функции для добавления, переключения и удаления задач.
  • Внутри компонента мы инициализируем состояние с помощью хука useReducer. Состояние управляется функцией reducer и начинается с initialState.
  • Мы также инициализируем другое состояние, input, используя хук useState, для обработки входного значения для добавления новых задач.

Обработка добавления задачи:

  • Функция handleAddTodo вызывается при нажатии кнопки «Добавить задачу».
  • Он проверяет, не является ли входное значение пустым (обрезанным), и если да, то отправляет действие типа 'ADD_TODO' с полезной нагрузкой, являющейся входным значением. Это добавляет новую задачу в массив состояний.
  • После добавления todo введенное значение очищается.

Визуализация списка задач:

  • Внутри оператора return мы отображаем поле ввода, кнопку «Добавить задачу» и список задач.
  • Поле input управляется состоянием input, и его значение обновляется обработчиком событий onChange.
  • При нажатии на кнопку «Добавить задачу» активируется функция handleAddTodo.
  • Todos сопоставляются с помощью функции map для создания элементов списка.
  • Каждый элемент списка задач отображается со своим текстом и кнопкой «Переключить».
  • Щелчок по тексту задачи переключает статус завершения задачи, отправляя действие 'TOGGLE_TODO' с идентификатором задачи.
  • Нажатие кнопки «Удалить» удаляет задачу, отправляя действие 'REMOVE_TODO' с идентификатором задачи.

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

Использование useReducer вместо нескольких хуков useState для сложного управления состоянием дает несколько преимуществ:

  • Простота. Благодаря централизации управления состоянием в функции-редюсере логика становится проще для анализа и обслуживания.
  • Гибкость. Благодаря структуре, которая разделяет задачи, проще добавлять новые действия и при необходимости корректировать логику изменения состояния.
  • Тестирование. Поскольку все изменения состояния могут обрабатываться в одной функции редуктора, тестирование упрощается, что упрощает выявление конкретных проблем.
  • Масштабируемость. При создании более крупных приложений, требующих более сложной конфигурации, useReducer может более эффективно обрабатывать большее количество состояний.

Заключение

Хук useReducer в React — это мощный инструмент, который упрощает сложное управление состоянием в функциональных компонентах. Определив функцию редюсера для обработки того, как состояние обновляется в ответ на действия, мы можем упростить наш код, упростив его понимание и тестирование. Мы также можем более эффективно обрабатывать множественные изменения состояния и повышать общую производительность нашего приложения.

Изучая useReducer, воспользуйтесь преимуществами этого отличного хука и организованного подхода к управлению состоянием в React.

Удачного кодирования с React!

Спасибо за чтение блога.

Следите за мной в:
LinkedIn — https://linkedin.com/in/joshiomkar04
Twitter — https://twitter.com/ye_joshya