Menu

Классовые компоненты React: state, жизненный цикл, переход

Классовый компонент это класс JavaScript, который наследует React.Component и возвращает JSX из метода render. this.props, this.state и setState, привязка обработчиков событий, методы жизненного цикла, PureComponent и как перевести класс в функциональный компонент.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Классовый компонент это класс JavaScript, который наследует React.Component и возвращает JSX из метода render(). Он читает пропсы из this.props, хранит состояние в this.state и обновляет его через this.setState(). В новом коде их заменили функциональные компоненты с хуками, но классы по-прежнему полностью поддерживаются, встречаются во многих старых проектах и остаются единственным способом написать границу ошибок.

Каждый счётчик хранит своё состояние. Добавьте третий <Counter label="Plums" />, и он тоже начнёт с 0.

Устройство классового компонента

import { Component } from 'react';

class Welcome extends Component {
    static defaultProps = { name: 'stranger' };

    constructor(props) {
        super(props); // required before using this
        this.state = { visits: 1 };
    }

    render() {
        return <h1>Hello, {this.props.name}. Visit #{this.state.visits}</h1>;
    }
}
  • Класс наследует Component (или React.Component с import React from 'react').
  • render() обязателен. Он должен быть чистым, как тело функционального компонента: читать this.props и this.state и возвращать JSX.
  • this.props хранит пропсы. Они доступны только для чтения.
  • Состояние можно задать в constructor (сначала вызовите super(props)) или, как сейчас чаще, полем класса: state = { visits: 1 };.
  • static defaultProps по-прежнему работает для классов в React 19 (его убрали только для функциональных компонентов).

this.setState объединяет и может принимать функцию обновления

setState делает две вещи иначе, чем сеттер useState. Он объединяет переданный объект с текущим состоянием, поэтому вы обновляете только названные ключи. И, как любое обновление состояния в React, он применяется не сразу, поэтому используйте форму с функцией обновления, когда следующее значение зависит от предыдущего.

«+3 with objects» добавляет один, потому что все три вызова читают одно и то же this.state.likes. «+3 with updaters» добавляет три. «Move to Paris» сохраняет name и likes (объединение), но теряет zip, потому что объединение работает только на один уровень. Напишите address: { ...this.state.address, city: 'Paris' }, чтобы его сохранить. Необязательный второй аргумент setState выполняется после обновления, и там консоль показывает новый адрес.

Привязка обработчиков событий

Метод, переданный как onClick={this.handleClick}, позже вызывается как обычная функция, без своего объекта, поэтому this внутри него равен undefined.

Первая кнопка выводит TypeError о чтении setState у undefined. Вторая работает, потому что стрелочная функция в поле класса захватывает this при создании экземпляра. Более старое решение, которое всё ещё часто встречается в унаследованном коде, привязывает в конструкторе:

constructor(props) {
    super(props);
    this.state = { on: false };
    this.handleClick = this.handleClick.bind(this);
}

Стрелка прямо в строке, onClick={() => this.handleClick()}, тоже работает.

Методы жизненного цикла

Классы реагируют на монтирование, обновление и размонтирование через методы с фиксированными именами. Чаще всего вы увидите эти:

  • componentDidMount(): после того как первый рендер оказался в DOM. Запускайте таймеры, подписки, запросы.
  • componentDidUpdate(prevProps, prevState): после каждого обновления. Сравнивайте с предыдущими значениями, прежде чем что-то делать, иначе получите цикл.
  • componentWillUnmount(): перед удалением. Отмените то, что запустил componentDidMount.
  • shouldComponentUpdate(nextProps, nextState): верните false, чтобы пропустить рендер.

Страница о жизненном цикле компонента содержит полный список, порядок выполнения и компонент, который выводит каждый метод.

Перевод класса в функциональный компонент

Вот класс, который использует все три распространённых метода жизненного цикла. Он тикает каждую секунду, выводит сообщение, когда меняется его пропс label, и останавливает таймер при удалении.

А вот тот же компонент в виде функции. Каждый метод жизненного цикла становится частью эффекта, а связанные настройка и очистка стоят рядом, а не в двух разных методах.

Оба таймера считают одинаково и выводят сообщение при каждой смене подписи. Версия-функция также выводит сообщение один раз при монтировании, потому что эффект выполняется и после первого рендера. Шаги для любого перевода:

  1. this.props.x становится деструктурированным пропсом, { x }.
  2. Каждый ключ this.state становится отдельным useState. Помните, что сеттер заменяет, а не объединяет.
  3. Методы становятся обычными функциями внутри компонента. Привязка не нужна.
  4. componentDidMount, componentDidUpdate и componentWillUnmount становятся вызовами useEffect, сгруппированными по тому, что они делают, а не по тому, когда выполняются.
  5. createRef становится useRef, а static contextType с this.context становится useContext или use.

Границы ошибок оставьте классами; функционального эквивалента у них нет.

PureComponent

Обычный классовый компонент рендерится каждый раз, когда рендерится его родитель. PureComponent сначала сравнивает новые пропсы и состояние с предыдущими на один уровень вглубь и пропускает рендер, когда ничего не изменилось. Это классовый аналог React.memo.

Нажмите кнопку: выводится только PlainLabel rendered, потому что PureLabel видит тот же text. Передайте text={'hello ' + clicks} обоим, и оба будут рендериться при каждом клике.

Сравнение поверхностное, поэтому пропс, который при каждом рендере родителя становится новым объектом или функцией (style={{ color: 'red' }}, onClick={() => ...}), сводит его на нет. Это та же ловушка, что и с React.memo.

Что React 19 убрал из классов

Если вы поддерживаете старый код на классах, несколько шаблонов в React 19 больше не работают:

  • Строковые рефы (ref="input" с this.refs.input). Используйте createRef() или ref-колбэк.
  • Устаревший контекст (contextTypes, childContextTypes, getChildContext). Используйте createContext со static contextType.
  • propTypes игнорируются. Используйте TypeScript или собственные проверки.
  • findDOMNode больше нет. Вместо этого ставьте ref на элемент DOM.

Старые методы componentWillMount, componentWillReceiveProps и componentWillUpdate тоже устарели. Они всё ещё выполняются, и под старыми именами, и под именами с UNSAFE_ (UNSAFE_componentWillMount и так далее), но объявлены устаревшими, и StrictMode о них предупреждает; перенесите их код в componentDidMount, componentDidUpdate или getDerivedStateFromProps.

Часто задаваемые вопросы

Классовые компоненты в React устарели?

Нет. Они по-прежнему поддерживаются в React 19 и продолжат работать, но документация React рекомендует для нового кода функциональные компоненты с хуками. Для границ ошибок класс всё ещё нужен.

Чем классовые компоненты отличаются от функциональных?

Классовый компонент хранит состояние в this.state, обновляет его через this.setState и реагирует на жизненный цикл через именованные методы. Функциональный компонент использует для тех же задач хуки (useState, useEffect), обходится без this и короче.

setState обновляет состояние сразу?

Нет. this.setState планирует обновление, а React рендерит позже, поэтому сразу после вызова в this.state всё ещё старое значение. Передавайте функцию, this.setState((state) => ...), когда новое значение зависит от старого, или читайте результат в колбэке второго аргумента или в componentDidUpdate.

Почему this равен undefined в моём обработчике события?

Метод класса, переданный как onClick={this.handleClick}, вызывается без своего объекта, поэтому this равен undefined. Объявите обработчик как стрелочную функцию в поле класса (handleClick = () => { ... }) или привяжите его в конструкторе.

Что такое PureComponent?

Component, который пропускает рендер, когда его пропсы и состояние поверхностно равны предыдущим. Это классовая версия React.memo.

Иллюстрация языков программирования Coddy

Учитесь программировать с Coddy

НАЧАТЬ