Этапы жизненного цикла в React.js (Lifecycle methods)

Этапы жизненного цикла в React.js (Lifecycle methods)

React.js на текущий момент является очень популярным фреймворком для написания приложений. Я решил работать с ним, т.к. на рынке Украины найти работу с этим фреймворком в резюме — достаточно легко.

За время моей работы с React (с 15 версии), методы жизненного цикла (lifecycle methods) уже менялись неоднократно. В этом посте я раскажу про методы, которые актуальны на начало 2019 года.

Что такое жизненный цикл

Для начала, вообще разберемся, что такое жизненный цикл компонента в терминологии React.js.

По сути, жизненный этап компонента похож на жизненный цикл живых существ и обозначает какое-то внутреннее состояние объекта в текущий момент. Например, этам рождения можно сравнить с Mounting. В реакте Mounting обозначает процесс встраивания компонента в DOM нашей странички.

Время жизни можно сравнить с Updating. Т.е. наш объект постоянно изменяется в соотствии с тем, что происходит снаружи (props) и вунтри (state).

А момент смерти — это Unmounting. В React этот этап будет происходить, когда компонент удаляется из DOM.

В 16 венсии еще появился этап Catching (error handling) — это похоже на ситуацию, когда наш объект заболел. В разрезе программирования это происходит, когда в логике есть баг или что-то не работает ожидаемым образом.

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

Методы жизненного цикла Mounting (Lifecycle methods) React

constructor(props) {}

Зачем: пригодится для того, чтобы инициализировать какие-то переменные для нашего компонента. По большому счету, уже не так уж и нужен, т.к. в связи с новыми стандартами в js мы можем инициализировать переменные через «=» прямо в теле класса (инициализация state происходит вне конструктора и вместо bind используются стрелочные функции).

Если будете реализовывать constructor, то не забывайте самой первой строчкой написать «super(props);», чтобы передать props в родительский компонент и он мог так же правильно инициализировать ваш компонент.

Что принимает: параметры от родителя (объект props).

Что возвращает: ничего не стоит возвращать, здесь просто инициализируются свойства.

static getDerivedStateFromProps(props, state) {}

Зачем: Это этап жизненного цикла, вызываемый перед render. Здесь мы можем обновить наш state в соответствии с параметрами, которые пришли к нам от родителя (props).

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

Что принимает: параметры от родителя (props) и начальное состояние (state).

Что возвращает: null/undefined — если нам не нужно менять state, {} (новый объект) — если нам нужно обновить state.

render() {}

Зачем: На этом этапе мы создаем jsx разметку для нашего компонента.

Важно: не пытайтесь здесь выполнить setState или forceUpdate, т.к. вы можете создать бесконечный цикл.

Что принимает: ничего. Для доступа к внутреннему состоянию или родительским параметрам нужно использовать this.state и this.props.

Что возвращает: этот метод может возвращать достаточно много разных типов данных. Начнем с самого классического — jsx разметка — здесь мы просто описываем то, как должен выглядеть наш компонент в DOM. Еще одним популярным вариантом того, что можно вернуть являются null/boolean — в этом случае компонент ничего не отобразит в DOM дереве. Если же мы хотим получить несколько элементов без оборачивания их в лишний родительских тэг, то мы можем вернуть массив jsx елементов или обернуть их в fragment (пустой компонент). Так же мы можем вернуть портал — если нам нужно отобразить React елемент где-то вне текущего родительского тэга. Если вы вернете просто число или строку, то они будут отображены в DOM как обычные текстовые элементы.

componentDidMount() {}

Зачем: вызывается сразу после того, как отработал render и дает нам возможность подписаться на какие-то события из DOM или выполнить какие-нибудь манипуляции с DOM деревом.

Кроме того, в componentDidMount вы можете вызвать setState и обновление произойдет не заметно для пользователя. Это полезная возможность, если вам, например, нужно узнать допустимый размер для текущего div элемента и только потом уже соответствующим образом реагировать. Только будьте бдительны и не слишком часто это используйте, а то это плохо повлияет на производительность вашего приложения.

Что принимает: ничего.

Что возвращает: ничего.

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

getDerivedStateFromProps(props, state) {}

Тут все аналогично с тем, что было написано про этот метод жизненного цикла в разделе Mounting.

shouldComponentUpdate(nextProps, nextState) {}

Зачем: дает нам возможность реализовать обновление компонента только в том случае, когда state и props удовлетворяют некоторому условию. Например, у вас может обновляться родительский компонент и в некоторых случаях его дочерние элементы тоже могут обновляться, хотя и не должны были бы (их state & props не изменились). В таких случаях и пригодится нам этот метод.

Кстати, PureComponent реализует этот метод и сравнивает все поля в props & state через ===.

Важно: Не стоит добавлять в этот метод слишком много логики и проверок, иначе производительность вашего приложения может заметно упасть.

Что принимает: новые значения для props и state. А к предыдущим значениям он может обратиться через this.props и this.state.

Что возвращает: возвращает true, если компонент должен перерисоваться и false, если компонент не должен обновляться.

render() {}

Тут все аналогично с тем, что было написано про этот метод жизненного цикла в разделе Mounting.

getSnapshotBeforeUpdate(prevProps, prevState) {}

Зачем: Вызывается во время обновления нашего компонента — сразу после ‘render’, но еще до того, как DOM изменился. Может нам помочь отлавливать изменения в DOM при обновлении компонента.

Хорошим примером использования этого метода является реализация чата. Например, вы хотели бы добавить автоматический скрол вниз чата при появлении нового сообщения. В таком случае, вы в getSnapshotBeforeUpdate отлавливаете размер текущего списка сообщений, передаете его в componentDidUpdate и уже там, основываясь на высоте нового списка элементов — просто скролите в нужное место.

Что принимает: старые props & state, так же по ref вы получите доступ к старым «отрисованным» элементам.

Что возвращает: null, если ничего не нужно передавать в componentDidUpdate. Или же любое значение из js (объект, число, строка и т.д), которое вы потом будете обрабатывать в componentDidMount, т.к. getSnapshotBeforeUpdate не должен ничего обновлять.

componentDidUpdate(prevProps, prevState, snapshot) {}

Зачем: Этот метод жизненного цикла дает нам возможность выполнять какие-то манипуляции с DOM, делать запросы и что-либо еще, что должно быть выполнено после того, как наш компонент обновился и отобразился в DOM.

Из этого метода можно вызывать setState, но стоит его обернуть в if, т.к. в противном случае вы получите бесконечный цикл обновления.

Что принимает: предыдущие значения state & props, а так же snapshot, который был передан из getSnapshotBeforeUpdate.

Что возвращает: ничего.

Методы жизненного цикла Unmounting (Lifecycle methods) React

componentWillUnmount() {}

Зачем: метод вызывается непосредственно перед удалением компонента из дерева DOM и дает нам возможность выполнить отписки для событий или какие-нибудь действия, которые должны быть выполнены перед удалением компонента.

Например, в ранних версиях React, когда использовался Flux, без обертки-контейнера, то нужно было вручную отписываться от всех сторов, на обновление которых был подписан компонент.

Что принимает: ничего.

Что возвращает: ничего.

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

В React.js начиная с 16 версии появилась возможность отлавливать исключения в дочерних компонентах (с некоторыми ограничениями). А в следствии последних изменений — появлися даже второй метод. Так что сейчас разберемся с обоими.

Кстати, для обработки исключений обычно создается вообще отдельный компонент, который будет отвечать только за обработку исключений. Обычно, такой компонент в React называют *Boundary (например, ErrorBoundary\AppBoundary\etc).

Где исключения не будут обработаны — в коде, который выполняется в Promise (запросы на сервер и т.п.), в setTimeout/setInterval. Исключения в React компонентах можно отлавливать только если они проихошли где-то в ходе выполнения самого компонента.

static getDerivedStateFromError(error) {}

Зачем: Используется для того, чтобы обновить состояние текущего компонента (ErrorBoundary), когда произошла ошибка в одном из дочерних компонентов.

Что принимает: объект ошибки, который был получен от дочернего элемента.

Что возвращает: объект, который будет устанавливать новое значение для state текущего компонента.

componentDidCatch(error, info) {}

Зачем: отлавливает исключения в дочерних элементах, но при этом позволяет еще выполнять «побочные эффекты» (side effects) — например, тут вы можете добавить логирование или еще какую-либо реакцию на исключение.

Что принимает: объект с ошибкой (error) и объект, содержащий дополнительную информацию о произошедшем исключении (info).

Что возвращает: ничего.


На этом мой рассказ про этапы жизненного цикла компонента в React.js можно считать оконченым. Так же, я бы хотел напомнить и про другие статьи, которые я уже добавлял на свой блог — немного теории по React Native и мой первый отзыв, а так же немного про CSRF и как предотвратить эту проблему.

Кроме того, здесь я еще делюсь отзывами про путешествия и фотографию, так что могу порекоммендовать к прочтению: как выбрать фотоаппарат и мой обзор sony a6000, а так же отзыв по городу Лиссабон, обзор Копенгагена, фото из слоновника в Пиннавеле.