Распространенный вопрос в моих курсах React касается использования хуков вместо классов, особенно когда речь идет об управлении состоянием компонента. Многие опытные разработчики React не решаются принять новый API хуков и придерживаются подхода на основе классов, с которым им удобнее. Хотя нет ничего плохого в том, чтобы продолжать использовать классы (React продолжит их полную поддержку), использование хуков на самом деле упрощает написание и поддержку кода.

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

Если вы предпочитаете смотреть живую демонстрацию этого кода, посмотрите мое видео на YouTube.

Ниже приведен пример компонента на основе классов, использующего локальное состояние для сбора данных из формы. Компонент следует обычному шаблону инициализации состояния в конструкторе свойства state компонента, а также необходимой привязке обработчиков событий для сохранения правильного значения this.

import React from 'react';
export class PersonFormClass extends React.Component {
constructor(props) {
    super(props);
this.state = {
      firstName: '',
      lastName: '',
    };
this.change = this.change.bind(this);
    this.displayForm = this.displayForm.bind(this);
  }
change(e) {
    this.setState({
      [ e.target.name ]: e.target.value,
    });
  }
displayForm() {
    console.log(this.state);
this.setState({
      firstName: '',
      lastName: '',
    });
  }
render() {
return <form>
      <h2>Person Form Class</h2>
      <div>
        <label htmlFor="first-name-input">First Name:</label>
        <input type="text" id="first-name-input" name="firstName"
          value={this.state.firstName} onChange={this.change} />
      </div>
      <div>
        <label htmlFor="last-name-input">Last Name:</label>
        <input type="text" id="last-name-input" name="lastName"
          value={this.state.lastName} onChange={this.change} />
      </div>
      <button type="button" onClick={this.displayForm}>Display Person Info</button>
    </form>;
  }
}

В то время как вышеприведенный код работает, отлично работает и выполняет свою работу, он немного многословен и требует эзотерических знаний JavaScript, чтобы полностью понять, почему все работает так, как работает, особенно код `bind` для обработчиков событий.

Альтернативный подход — использовать хуки вместо компонента на основе классов, как показано ниже. При использовании хуков вызывается хук useState, который возвращает данные состояния и функцию для обновления состояния (которая также запускает повторный рендеринг, как и setState из класса компоненты на основе). Хук useState возвращает данные и функцию обновления в виде элементов массива, который затем можно деструктурировать, как показано ниже (деструктуризация массива используется для облегчения настройки имен переменных для данных и функции обновления). .

Кроме того, вы заметите, что нет необходимости в конструкторе, в синтаксисе bind и т. д. Просто простые функции, и нет необходимости вычислять значение this.

export const PersonFormHook = () => {
const [ personForm, setPersonForm ] = useState({
    firstName: '',
    lastName: '',
  });
const change = (e) => {
    setPersonForm({
      ...personForm,
      [ e.target.name ]: e.target.value,
    });
  }
const displayForm = () => {
    console.log(personForm);
setPersonForm({
      firstName: '',
      lastName: '',
    });
  }
return <form>
    <h2>Person Form Hook</h2>
    <div>
      <label htmlFor="first-name-input">First Name:</label>
      <input type="text" id="first-name-input" name="firstName"
        value={personForm.firstName} onChange={change} />
    </div>
    <div>
      <label htmlFor="last-name-input">Last Name:</label>
      <input type="text" id="last-name-input" name="lastName"
        value={personForm.lastName} onChange={change} />
    </div>
    <button type="button" onClick={displayForm}>Display Person Info</button>
  </form>;
};

Что еще лучше, так это то, что большая часть кода состояния в подходе с хуками может быть перемещена в пользовательский хук, как показано ниже. Пользовательский хук можно настроить для возврата данных и пользовательских функций, необходимых для формы, таких как функция change и/или функция сброса формы.

import { useState } from 'react';
export const useForm = initialForm => {
const [ form, setForm ] = useState(initialForm);
const change = (e) => {
    setForm({
      ...form,
      [ e.target.name ]: e.target.value,
    });
  };
return [ form, change, () => setForm(initialForm) ];
};

Пользовательский хук затем можно импортировать в функциональный компонент и использовать вместо хука useState, как показано ниже. Функции, специфичные для формы, теперь импортируются и могут использоваться повторно для всех форм. Отлично, правда?

import React from 'react';
import { useForm } from '../hooks/useForm';
export const PersonFormCustomHook = () => {
const [ personForm, change, resetPersonForm ] = useForm({
    firstName: '',
    lastName: '',
  });
const displayForm = () => {
    console.log(personForm);
resetPersonForm();
  }
return <form>
    <h2>Person Form Custom Hook</h2>
    <div>
      <label htmlFor="first-name-input">First Name:</label>
      <input type="text" id="first-name-input" name="firstName"
        value={personForm.firstName} onChange={change} />
    </div>
    <div>
      <label htmlFor="last-name-input">Last Name:</label>
      <input type="text" id="last-name-input" name="lastName"
        value={personForm.lastName} onChange={change} />
    </div>
    <button type="button" onClick={displayForm}>Display Person Info</button>
  </form>;
};

Как видите, код для хуков гораздо легче читать и поддерживать. Кроме того, он позволяет легко создавать повторно используемую логику с отслеживанием состояния. Я рекомендую использовать хуки для всех новых компонентов и включать их в новые и существующие деревья компонентов. В то время как хуки можно использовать только внутри функциональных компонентов, как основанные на классах, так и функциональные компоненты (включая те, которые используют хуки) могут использоваться в одном и том же дереве компонентов.

**Популярность хуков растет!** Вы увидите, как они используются в общих библиотеках React, таких как React Router, Apollo Client и React-Redux!

Скачать код

Чтобы использовать код, распакуйте zip, откройте окно терминала и перейдите в извлеченную папку. Убедитесь, что вы находитесь в той же папке, что и файл package.json. Запустите команду npm install, затем запустите npm start. Когда приложение React запускается, оно открывает веб-страницу React в веб-браузере по умолчанию. Используйте редактор по вашему выбору, чтобы поэкспериментировать с кодом.