React Классы

До версии React 16.8 компоненты-классы были единственным способом отслеживать состояние и жизненный цикл компонента. Компоненты-функции воспринимались как "без состояния".

С появлением хуков компоненты-функции стали почти эквивалентны классам. Различия между ними столь малы, что, вероятно, вам никогда не потребуется использовать компоненты-классы в React.

Хотя сейчас рекомендуется использовать компоненты-функции, полное изъятие компонентов-классов пока не планируется.

Данный урок посвящен тому, как использовать компоненты-классы в React.

Если вы планируете использовать только компоненты-функции, вы можете пропустить этот урок.

Что такое компоненты в React?

Компоненты — это автономные и многократно используемые фрагменты кода. Они выполняют ту же роль, что и функции JavaScript, но функционируют независимо и возвращают HTML через метод render.

Компоненты бывают двух типов: классы и функции. В этом уроке мы будем изучать компоненты-классы.


Создание компонентов-классов

При создании компонента в React имя компонента обязано начинаться с заглавной буквы.

Компонент должен содержать объявление наследования extends React.Component, что устанавливает связь с базовым классом React.Component и открывает доступ к его методам.

Компоненту также необходим метод render(), возвращающий HTML.

Пример

Создаем компонент-класс под названием Car:


class Car extends React.Component {
  render() {
    return <h2>Привет, я - автомобиль!</h2>;
  }
}

Теперь у нас есть компонент Car, возвращающий элемент <h2>.

Чтобы использовать этот компонент в приложении, обратитесь к нему так: <Car />.

Пример

Отобразим компонент Car в элементе "root":


createRoot(document.getElementById('root')).render(
  <Car />
);


Конструктор компонента

Если в компоненте присутствует конструктор (constructor()), он вызывается при инициализации компонента.

Конструктор — это место, где устанавливаются свойства компонента. В React состояние компонента хранится в специальном объекте state.

Кроме того, конструктор — это точка, где нужно вызвать метод родителя super(), передавая в него props, чтобы унаследовать методы родительского компонента (React.Component).

Пример

Создадим конструктор в компоненте Car и добавим свойство color:


class Car extends React.Component {
  constructor() {
    super();
    this.state = {color: "красный"};
  }
  render() {
    return <h2>Я - автомобиль!</h2>;
  }
}

Используем свойство color в методе render:

Пример


class Car extends React.Component {
  constructor() {
    super();
    this.state = {color: "red"};
  }
  render() {
    return <h2>Я - {this.state.color} автомобиль!</h2>;
  }
}


Пропсы (props)

Другой способ управления свойствами компонента — использование пропсов (props).

Пропсы подобны аргументам функций, и передаются в компонент в виде атрибутов.

Подробнее о пропсах вы узнаете в следующей главе.

Пример

Передача атрибута color компоненту Car и использование его в методе render:


class Car extends React.Component {
  render() {
    return <h2>Я - {this.props.color} автомобиль!</h2>;
  }
}

createRoot(document.getElementById('root')).render(
  <Car color="красный" />
);


Пропсы в конструкторе

Если компонент имеет конструктор, пропсы должны быть переданы конструктору и родительскому классу через метод super().

Пример


class Car extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return <h2>Я {this.props.model}!</h2>;
  }
}

createRoot(document.getElementById('root')).render(
  <Car model="Мустанг" />
);


Компоненты внутри компонентов

Компоненты можно вызывать внутри других компонентов.

Пример

Используем компонент Car внутри компонента Garage:


class Car extends React.Component {
  render() {
    return <h2>Я автомобиль!</h2>;
  }
}

class Garage extends React.Component {
  render() {
    return (
      <div>
        <h1>Кто живет в моем гараже?</h1>
        <Car />
      </div>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Garage />
);


Компоненты в файлах

React направлен на повторное использование кода, и разумно размещать некоторые компоненты в отдельных файлах.

Для этого создайте новый файл с расширением .jsx и поместите в него код компонента.

Обратите внимание, что файл должен начинаться с импорта React и завершаться конструкцией export default Car;.

Пример

Наш новый файл называется Vehicle.jsx:


import React from 'react';

class Car extends React.Component {
  render() {
    return <h2>Привет, я автомобиль!</h2>;
  }
}

export default Car;

Чтобы использовать компонент Car, необходимо импортировать файл Vehicle.jsx в ваше приложение.

Пример

Импортируем файл Vehicle.jsx и используем компонент Car:


import { createRoot } from 'react-dom/client';
import Car from './Vehicle.jsx';

createRoot(document.getElementById('root')).render(
  <Car />
);


Внутреннее состояние компонента

Компоненты-классы имеют встроенный объект состояния (state).

Ранее мы уже использовали объект state в конструкторе компонента.

Объект состояния state используется для хранения значений свойств, принадлежащих самому компоненту.

При изменении объекта состояния state, компонент перерисовывается.


Создание объекта состояния state

Объект состояния state инициализируется в конструкторе.

Пример

Установка объекта state в методе constructor:


class Car extends React.Component {
  constructor(props) {
    super(props);
    this.state = {brand: "Ford"};
  }
  render() {
    return (
      <div>
        <h1>Моя машина</h1>
      </div>
    );
  }
}

Объект состояния state может содержать столько свойств, сколько необходимо:

Пример

Определение всех необходимых компоненту свойств:


class Car extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      brand: "Ford",
      model: "Mustang",
      color: "красный",
      year: 1964
    };
  }
  render() {
    return (
      <div>
        <h1>Моя машина</h1>
      </div>
    );
  }
}


Использование объекта состояния state

Обратиться к объекту состояния state можно в любом месте компонента, используя синтаксис this.state.<имя_свойства>:

Пример

Обращаемся к объекту state в методе render:


class Car extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      brand: "Ford",
      model: "Mustang",
      color: "красный",
      year: 1964
    };
  }
  render() {
    return (
      <div>
        <h1>Мой автомобиль {this.state.brand}</h1>
        <p>Это мой {this.state.color} {this.state.model} {this.state.year} года.</p>
      </div>
    );
  }
}


Изменение объекта состояния state

Для изменения значений в объекте state используется метод this.setState().

При изменении объекта состояния state компонент автоматически перерисовывает свое содержимое, реагируя на новое значение.

Пример

Добавим кнопку с событием onClick, которая изменяет цвет автомобиля:


class Car extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      brand: "Ford",
      model: "Mustang",
      color: "красный",
      year: 1964
    };
  }
  changeColor = () => {
    this.setState({color: "синий"});
  }
  render() {
    return (
      <div>
        <h1>Мой {this.state.brand}</h1>
        <p>Это мой {this.state.color} {this.state.model} {this.state.year} года.</p>
        <button
          type="button"
          onClick={this.changeColor}
        >Изменить цвет</button>
      </div>
    );
  }
}

Всегда используйте метод setState() для изменения состояния, так как он уведомляет компонент о необходимости обновления и вызывает метод render().

Жизненный цикл компонентов

У каждого компонента есть свой жизненный цикл, три фазы которого вы можете отслеживать и манипулировать.

Три основные фазы жизненного цикла компонента: монтирование, обновление и демонтирование.


Монтирование

Монтирование — это этап размещения элементов в DOM.

React последовательно вызывает четыре встроенных метода при монтировании компонента:

  1. constructor()
  2. getDerivedStateFromProps()
  3. render()
  4. componentDidMount()

Метод render() обязателен и вызывается всегда, остальные методы вызываются при необходимости.


Метод constructor()

Метод constructor() вызывается до любых других методов, при инициации компонента. Это идеальное место для установки первоначальных значений объекта состояния state и других начальных значений.

Метод вызывается с передачей аргумента props, и первым действием должен быть вызов super(props) для инициализации конструктора родительского компонента и наследования всех методов родителя (React.Component).

Пример

Метод constructor() вызывается при каждом создании компонента:


class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {favoritecolor: "красный"};
  }
  render() {
    return (
      <h1>Мой любимый цвет — {this.state.favoritecolor}</h1>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header />
);


Метод getDerivedStateFromProps()

Метод getDerivedStateFromProps() вызывается непосредственно перед рендерингом элемента в DOM.

Это подходящее место для установки объекта состояния state на основе первоначальных значений props (пропсов).

Он принимает в качестве аргумента объект state и возвращает его измененным.

Пример

Начальный цвет "красный", но метод getDerivedStateFromProps() обновляет цвет на основе атрибута favcol:


class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {favoritecolor: "красный"};
  }
  static getDerivedStateFromProps(props, state) {
    return {favoritecolor: props.favcol };
  }
  render() {
    return (
      <h1>Мой любимый цвет — {this.state.favoritecolor}</h1>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header favcol="жёлтый" />
);

Метод getDerivedStateFromProps вызывается непосредствено перед методом render.


Метод render()

Метод render() обязателен и отвечает за фактическое размещение HTML в DOM.

Пример

Простой компонент с простым методом render():


class Header extends React.Component {
  render() {
    return (
      <h1>Это содержимое компонента Header</h1>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header />
);


Метод componentDidMount()

Метод componentDidMount() вызывается после завершения рендеринга компонента.

Это место для выполнения команд, требующих, чтобы компонент уже находился в DOM.

Пример

Сначала мой любимый цвет красный, но через секунду он станет жёлтым:


class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {favoritecolor: "красный"};
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({favoritecolor: "жёлтый"})
    }, 1000)
  }
  render() {
    return (
      <h1>Мой любимый цвет — {this.state.favoritecolor}</h1>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header />
);


Обновление

Следующая фаза жизненного цикла наступает, когда компонент обновляется. Компонент обновляется всякий раз, когда изменяется его состояние или пропсы.

React последовательно вызывает пять встроенных методов при обновлении компонента:

  1. getDerivedStateFromProps()
  2. shouldComponentUpdate()
  3. render()
  4. getSnapshotBeforeUpdate()
  5. componentDidUpdate()

Метод render() обязателен и всегда вызывается, остальные методы выполняются при необходимости.


Метод getDerivedStateFromProps()

Метод getDerivedStateFromProps() также вызывается при обновлениях. Это первое, что происходит при обновлении компонента.

Это подходящее место для установки объекта состояния state на основе первоначальных пропсов.

В следующем примере есть кнопка, при нажатии на которую любимый цвет меняется на синий, но поскольку вызывается метод getDerivedStateFromProps(), который изменяет состояние компонента на основе атрибута favcol, то любимый цвет остается жёлтым.

Пример

Если компонент обновляется, вызывается метод getDerivedStateFromProps():


class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {favoritecolor: "красный"};
  }
  static getDerivedStateFromProps(props, state) {
    return {favoritecolor: props.favcol };
  }
  changeColor = () => {
    this.setState({favoritecolor: "синий"});
  }
  render() {
    return (
      <div>
        <h1>Мой любимый цвет — {this.state.favoritecolor}</h1>
        <button type="button" onClick={this.changeColor}>Изменить цвет</button>
      </div>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header favcol="жёлтый" />
);


Метод shouldComponentUpdate()

В методе shouldComponentUpdate() можно вернуть булевое значение, указывающее, должен ли React продолжать рендеринг или нет.

Значение по умолчанию — true.

Следующий пример показывает, что происходит, когда метод shouldComponentUpdate() возвращает false.

Пример

Остановим компонент от рендеринга при любых изменениях:


class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {favoritecolor: "красный"};
  }
  shouldComponentUpdate() {
    return false;
  }
  changeColor = () => {
    this.setState({favoritecolor: "синий"});
  }
  render() {
    return (
      <div>
        <h1>Мой любимый цвет — {this.state.favoritecolor}</h1>
        <button type="button" onClick={this.changeColor}>Изменить цвет</button>
      </div>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header />
);

Пример

Такой же, как и предыдущий пример, но метод shouldComponentUpdate() возвращает true:


class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {favoritecolor: "красный"};
  }
  shouldComponentUpdate() {
    return true;
  }
  changeColor = () => {
    this.setState({favoritecolor: "синий"});
  }
  render() {
    return (
      <div>
        <h1>Мой любимый цвет — {this.state.favoritecolor}</h1>
        <button type="button" onClick={this.changeColor}>Изменить цвет</button>
      </div>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header />
);


Метод render()

Метод render() обязательно вызывается при обновлении компонента, так как необходимо заново отрендерить HTML в DOM с новыми изменениями.

В следующем примере есть кнопка, которая меняет любимый цвет на синий.

Пример

При нажатии кнопки меняется состояние компонента:


class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {favoritecolor: "красный"};
  }
  changeColor = () => {
    this.setState({favoritecolor: "синий"});
  }
  render() {
    return (
      <div>
        <h1>Мой любимый цвет — {this.state.favoritecolor}</h1>
        <button type="button" onClick={this.changeColor}>Изменить цвет</button>
      </div>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header />
);


Метод getSnapshotBeforeUpdate()

Метод getSnapshotBeforeUpdate() позволяет получить доступ к свойствам props и состоянию state до обновления, что означает, что после обновления можно проверить предыдущие значения.

Если метод getSnapshotBeforeUpdate() присутствует, вы также должны реализовать метод componentDidUpdate(), иначе возникнет ошибка.

Следующий пример может показаться сложным, но всё, что он делает, сводится к следующему:

  • Когда компонент монтируется, он рендерится с любимым цветом "красный".
  • После того, как компонент смонтировался, срабатывает таймер, изменяющий состояние, и спустя одну секунду любимый цвет становится "жёлтым".
  • Это действие запускает фазу обновления, и поскольку у компонента имеется метод getSnapshotBeforeUpdate(), этот метод исполняется и выводит сообщение в пустой элемент DIV1.
  • Затем исполняется метод componentDidUpdate(), который выводит сообщение в пустой элемент DIV2.

Пример

Используем метод getSnapshotBeforeUpdate(), чтобы выяснить, как выглядел объект состояния state до обновления:


class Header extends React.Component {
  constructor(props) {
    super(props);
    this.state = {favoritecolor: "красный"};
  }
  componentDidMount() {
    setTimeout(() => {
      this.setState({favoritecolor: "жёлтый"})
    }, 1000)
  }
  getSnapshotBeforeUpdate(prevProps, prevState) {
    document.getElementById("div1").innerHTML =
    "До обновления любимым цветом был " + prevState.favoritecolor;
  }
  componentDidUpdate() {
    document.getElementById("div2").innerHTML =
    "Обновлённый любимый цвет — " + this.state.favoritecolor;
  }
  render() {
    return (
      <div>
        <h1>Мой любимый цвет — {this.state.favoritecolor}</h1>
        <div id="div1"></div>
        <div id="div2"></div>
      </div>
    );
  }
}

createRoot(document.getElementById('root')).render(
  <Header />
);


Демонтирование

Фаза демонтирования жизненного цикла компонента React наступает тогда, когда компонент удаляется из дерева DOM, то есть становится ненужным и выводится из отображаемого интерфейса. Это важно понимать, поскольку любые активные процессы, подписки, таймеры или сетевые запросы, запущенные компонентом ранее, должны быть завершены, иначе возможны утечки памяти или ошибки.

В классической модели компонента-класса React существует только один метод, связанный непосредственно с фазой демонтирования — это componentWillUnmount.


Метод componentWillUnmount

Этот метод используется для очистки всех ресурсов, используемых компонентом, таких как:

  • Отмена сетевых запросов.
  • Остановка активных таймеров (setTimeout, setInterval).
  • Удаление обработчиков событий.
  • Отмена подписок на внешние сервисы (например, хранилища Redux или WebSocket-подключения).

Пример

Реализации метода componentWillUnmount:


class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { loading: true };
    // Создание интервала обновления состояния каждые 5 секунд
    this.intervalId = setInterval(() => {
      this.setState({ loading: false });
    }, 5000);
  }

  componentWillUnmount() {
    clearInterval(this.intervalId); // очистка интервала перед размонтировкой
  }

  render() {
    return (
      <div>{this.state.loading ? 'Загрузка...' : 'Готово!'}</div>
    );
  }
}

Важно помнить, что начиная с версии React 16.3+, предпочтительным способом управления состоянием является использование хуков (useEffect), где управление ресурсами реализовано проще и удобнее.

Таким образом, фаза демонтирования обеспечивает аккуратное завершение работы компонента, предотвращая возможные проблемы производительности и ошибок в приложении.