В React управление состоянием компонентов всегда было проблемой. Традиционно использовались компонент класса и подход жизненного цикла, но этот подход приводит к избыточному коду, который трудно поддерживать. Появление React Hooks решает эту проблему, позволяя нам реализовать управление состоянием аналогично компонентам классов в функциональных компонентах. В этой статье я покажу вам, как использовать React Hooks для реализации управления состоянием сложных компонентов.

Основы управления состоянием — useState Hook

UseState — один из наиболее часто используемых хуков, которые позволяют нам определять состояние в функциональном компоненте и обновлять его в компоненте. Вот простой пример:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  function increment() {
    setCount(count + 1);
  }
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

В приведенном выше коде мы определяем состояние счетчика с помощью useState и обновляем его с помощью метода setCount. Каждый раз, когда нажимается кнопка Increment, значение count увеличивается на 1.

Расширенный уровень управления состоянием — useReducer Hook

Когда состояние компонента сложное, использование useState может привести к путанице в коде. На этом этапе рассмотрите возможность использования useReducer. UseReducer — это еще один способ управления состоянием, который позволяет нам лучше организовать наш код и поместить логику для обновления состояния внутри функции редуктора.

import { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
}

В приведенном выше коде мы определяем счетчик состояний с помощью useReducer и помещаем логику обновления состояния в функцию редуктора. Каждый раз, когда нажимается кнопка «Увеличить» или «Уменьшить», запускается соответствующее действие.

Практика — реализация TodoList с использованием React Hooks

После введения useState и useReducer выше, давайте реализуем компонент TodoList. Этот компонент имеет следующие функции:

  • Отображает все элементы списка задач
  • Добавить новый элемент списка дел
  • Удалить выполненный элемент задачи

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

function TodoItem({ text, completed, onClick }) {
  return (
    <li style={{ textDecoration: completed ? 'line-through' : 'none' }} onClick={onClick}>
      {text}
    </li>
  );
}

Далее мы можем начать писать компонент TodoList. Поскольку нам нужно управлять состоянием нескольких элементов todo, более уместно использовать useReducer.

import { useReducer } from 'react';
import TodoItem from './TodoItem';

const ADD_TODO = 'ADD_TODO';
const TOGGLE_TODO = 'TOGGLE_TODO';
const DELETE_TODO = 'DELETE_TODO';
function reducer(state, action) {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state,
        {
          id: state.length + 1,
          text: action.text,
          completed: false,
        },
      ];
    case TOGGLE_TODO:
      return state.map(todo =>
        todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      );
    case DELETE_TODO:
      return state.filter(todo => todo.id !== action.id);
    default:
      throw new Error();
  }
}

export default function TodoList() {
  const [state, dispatch] = useReducer(reducer, []);
  function handleAdd(text) {
    dispatch({ type: ADD_TODO, text });
  }
  function handleToggle(id) {
    dispatch({ type: TOGGLE_TODO, id });
  }
  function handleDelete(id) {
    dispatch({ type: DELETE_TODO, id });
  }
  return (
    <div>
      <ul>
        {state.map(todo => (
          <TodoItem
            key={todo.id}
            text={todo.text}
            completed={todo.completed}
            onClick={() => handleToggle(todo.id)}
          />
        ))}
      </ul>
      <input type="text" placeholder="Add a new todo" onKeyDown={e => e.key === 'Enter' && handleAdd(e.target.value)} />
      <button onClick={() => handleDelete()}>Delete Completed Todos</button>
    </div>
  );
}

В приведенном выше коде мы определяем три типа действий, соответствующих разным операциям, и обрабатываем соответствующую логику в функции редуктора. В компоненте TodoList мы определяем состояние задач с помощью useReducer и предоставляем три метода handleAdd, handleToggle и handleDelete для добавления, переключения и удаления элементов задач. В то же время мы также отображаем элемент ввода и элемент кнопки для добавления новых элементов списка задач и удаления завершенных элементов списка задач соответственно.

На данный момент мы реализовали простой компонент TodoList. Использование useReduce позволяет нам лучше организовать наш код и сделать его более понятным.

Краткое содержание

React Hooks обеспечивают более лаконичный способ управления состоянием компонентов. Использование useState и useReduce позволяет нам лучше организовать наш код и улучшить его читабельность и удобство сопровождения.

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.