Зарегистрируйтесь, чтобы продолжить обучение

Жизненный цикл компонента JS: React

При правильном использовании React большая часть компонентов состоит из метода render и обработчиков событий:

class ArticleItem extends React.Component {
  handleClick = (e) => {
    e.preventDefault()
    const { onClick } = this.props
    onClick()
  }

  render() {
    const { name, description, link } = this.props
    return (
      <div>
        <a href={link} onClick={this.handleClick}>{name}</a>
        <br />
        <div>{description}</div>
      </div>
    )
  }
}

// Пример использования ArticleItem:
// <ArticleItem
//   name="name"
//   description="This is description"
//   link="#"
//   onClick={someFunction}
// />

Но не все задачи решаются так просто. Представьте себе компонент <Clock />, имитирующий цифровые часы в формате чч:мм:сс. Заготовка:

class Clock extends React.Component {
  render() {
    const currentTime = new Date()
    return (
      <div>{currentTime.toLocaleTimeString()}</div>
    )
  }
}

Этот компонент отображает текущее время. Теперь нужно придумать, как его обновлять. Часы, в отличие от обычных компонентов, не ожидают действий от пользователя. Они обновляются каждую секунду самостоятельно. Возникает цепочка: происходит событие => меняется текущее время => React вызывает render и меняет DOM. Итак, состояние инициализируется текущим временем:

class Clock extends React.Component {
  constructor(props) {
    super(props)
    this.state = { date: new Date() }
  }

  render() {
    const { date } = this.state
    return (
      <div>{date.toLocaleTimeString()}</div>
    )
  }
}

Компонент по-прежнему показывает текущее время на момент отрисовки/перерисовки компонента, но теперь он готов к изменению. Время относится к периодическим событиям, для которых используются таймеры. Для <Clock /> подойдет setInterval. Таймер должен быть установлен сразу после отрисовки часов и должен быть очищен при удалении компонента из дерева элементов.

setInterval(() => this.setState({ date: new Date() }), 1000)

Где запускать таймер? render вызывается на каждое изменение состояния, а значит он не подходит. Ведь тогда <Clock /> будет запускать новый таймер каждую секунду. Конструктор кажется более подходящим местом, но здесь ожидает сюрприз. Вызов конструктора и отрисовка часов в DOM-дереве, в общем случае — два независимых события. Посмотрите на код:

// Вызывается конструктор
const clock = <Clock />

// Что-то долго делаем еще

// Отрисовываем
const root = createRoot(document.getElementById('root'))
root.render(clock)

Эти часы еще не находятся в DOM-дереве, но уже вовсю работают и обновляются. Стоит ли об этом беспокоиться? Да, такое поведение крайне неожиданно, оно мешает тестированию и расходует процессорное время. Кроме того, конструктор никак не помогает с удалением таймера.

Каждый компонент React проходит несколько стадий в процессе своей жизни: он создается, затем добавляется в DOM, затем может обновляться (например, при изменении пропсов или состояния), и, наконец, удаляется из дерева. Этот процесс называют жизненным циклом компонента (Component Lifecycle). React предоставляет набор методов, которые позволяют встроиться в этот процесс. Например, запуск часов логичнее всего сделать сразу после их отрисовки. В этом поможет метод componentDidMount. Он вызывается сразу после отрисовки компонента. Происходит это ровно один раз.

class Clock extends React.Component {
  constructor(props) {
    super(props)
    this.state = { date: new Date() }
  }

  componentDidMount() {
    // Сохраняется идентификатор таймера
    this.timerId = setInterval(() => this.setState({ date: new Date() }), 1000)
  }

  render() {
    const { date } = this.state
    return (
      <div>{date.toLocaleTimeString()}</div>
    )
  }
}

Обратите внимание на то, как сохраняется идентификатор таймера внутри объекта. Он не участвует в представлении, поэтому нет необходимости сохранять его в состоянии.

Теперь нужно выполнить очистку таймера. Для этого подойдет метод componentWillUnmount, который выполняется прямо перед удалением компонента из DOM.

class Clock extends React.Component {
  constructor(props) {
    super(props)
    this.state = { date: new Date() }
  }

  componentDidMount() {
    this.timerId = setInterval(() => this.setState({ date: new Date() }), 1000)
  }

  componentWillUnmount() {
    clearInterval(this.timerId)
  }

  render() {
    const { date } = this.state
    return (
      <div>{date.toLocaleTimeString()}</div>
    )
  }
}

Часы приобрели законченный вид.

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

Монтирование (Mounting)

Эти методы вызываются по порядку во время создания объекта и вставки его в DOM.

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

Обновление (Updating)

Обновление может происходить при изменении свойств или состояния. Эти методы вызываются по порядку во время перерисовки:

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

Удаление или демонтирование (Unmounting)

В эту группу входит один метод. Он вызывается во время удаления компонента из DOM.

  • componentWillUnmount()

Такое количество методов объясняется сложностью реальной разработки. Но на практике лишь некоторые используются регулярно. К таким методам относится componentDidMount. С его помощью устанавливают таймеры, выполняют AJAX-запросы, меняют DOM в обход React. Последнее бывает нужно при интеграции со сторонними библиотеками.


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

  1. Методы жизненного цикла
  2. Зачем нужно частичное применение в обработчиках событий?

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff