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

В приложениях с множеством компонентов очень важно освобождать используемые системные ресурсы когда компоненты удаляются.

  • Первоначальный рендеринг компонента в DOM называется «монтирование» (mounting).
  • Каждый раз когда DOM-узел, созданный компонентом, удаляется, происходит «размонтирование» (unmounting).

constructor(props)

Конструктор, в котором происходит начальная инициализация компонента.

Если вы не инициализируете состояние и не привязываете методы, вам не нужно реализовывать конструктор для вашего компонента React. Конструктор для компонента React вызывается до его монтирования. При реализации конструктора для подкласса React.Component вы должны вызывать super(props) перед любым другим оператором. В противном случае this.props будет неопределенным в конструкторе, что может привести к ошибкам. Как правило, в React конструкторы используются только для двух целей: Инициализация локального состояния путем присвоения объекта this.state. Привязка методов обработчика событий к экземпляру. Вы не должны вызывать setState() в конструкторе ! Вместо этого, если ваш компонент должен использовать локальное состояние, присвойте начальное состояние this.state непосредственно в конструкторе:

constructor(props) {
super(props);
// Не вызывать setState, конструктор только для задания начального состояния
this.state = { counter: 0 };
}

Конструктор - это единственное место, где вы должны назначить this.state напрямую. Во всех других методах вам нужно использовать this.setState().

Избегайте копирования props в state! Это распространенная ошибка (особенно новичков):

constructor(props) {
super(props);
// Don't do this!!!!
this.state = { color: props.color };
}

Проблема заключается в том, что он не нужен (вместо этого можно использовать this.props.color напрямую), а также создает ошибки (обновления props.color не будут отражаться в state). Используйте этот шаблон, только если вы намеренно хотите игнорировать обновления props. В этом случае имеет смысл переименовать реквизит, чтобы он назывался initialColor или defaultColor. Затем вы можете принудительно заставить компонент «сбрасывать» свое внутреннее состояние, меняя ключ при необходимости.

componentWillMount()

Вызывается непосредственно перед рендерингом компонента. В данный момент у нас нет возможности посмотреть DOM элементы. componentWillMount не очень отличается от конструктора – он также вызывается лишь раз в изначальном жизненном цикле. Вообще исторически были некоторые причины использовать componentWillMount поверх конструктора, но на данный момент практика, описанная там, устарела. У многих возможно появится соблазн использовать эту функцию для отправки реквеста на получение данных и они будут ожидать, что данные будут доступны прежде чем отработает изначальный render. Но это не тот случай – хотя реквест и будет инициализирован перед render, он не успеет выполниться прежде чем render будет вызван. Кроме того, с изменениями в React Fiber (после релиза React 16 beta) эта функция может быть вызвана несколько раз перед вызовом изначального render, что может привести к различным побочным эффектам, связанным с этим. Поэтому, не рекомендуется использовать эту функцию для выполнения любых операций вызывающих сайд-эффекты. Также важно отметить, что эта функция вызывается, когда используется рендеринг на стороне сервера (server side rendering), когда ее антипод – componentDidMount не будет вызван на сервере, но будет на клиенте. Поэтому если некоторый сайд-эффект нацелен на серверную часть, эта функция может быть использована как исключение. И наконец функция setState может быть свободно использована и не будет вызывать перерисовку компонента.

В componentWillMount можно:

  • Обновляйть состояние через this.setState
  • Выполняйть последние оптимизации
  • Вызывайть сайд-эффекты (Вызов AJAX и т.д.) только в случае server-side-rendering.

РЕКОМЕНДУЕТСЯ не делать:

  • Выполнять никие либо сайд-эффекты (Вызов AJAX и т.д.) на стороне клиента.
  • В данный момент этот метод жизненно цикла уже устарел и был устранен(deprecated)

Вы можете использовать старый componentWillMount только с приставкоый UNSAFE_componentWillMount()

getDerivedStateFromProps()

static getDerivedStateFromProps(nextProps, prevState) вызывается непосредственно перед вызовом метода рендеринга как при первоначальном монтировании, так и при последующих обновлениях. В качетве параметров получает:

  • nextProps - уже измененные данные, но компонент еще не был изменен с этими данными.
  • prevState

на основании этих входящих данных можно сделать сравнение и вернуть обьект прямо в state компонента без вызова функии setState().

state = { cachedSomeProp: null };
static getDerivedStateFromProps(nextProps, prevState) {
// do things with nextProps.someProp and prevState.cachedSomeProp
return {
cachedSomeProp: nextProps.someProp,
..
};
}

Этот метод существует для редких случаев использования, когда состояние зависит от изменений props с течением времени. Например, это может быть удобно для реализации компонента <Transition>, который сравнивает своих предыдущих и следующих потомков, чтобы решить, кого из них нужно анимировать. getDerivedStateFromProps должен возвращать объект, чтобы обновить состояние, или null, чтобы ничего не обновлять. В getDerivedStateFromProps методе нет доступа к this

Метод запускается при каждом рендере, независимо от причины.

render()

Метод render() является единственным обязательным методом в компоненте класса. При вызове он должен проверить this.props и this.state и вернуть один из следующих типов: React element. Обычно создается через JSX. Например, <div /> и <MyComponent /> являются элементами React, которые инструктируют React визуализировать узел DOM или другой пользовательский компонент, соответственно.

  • Массивы и фрагменты. Позволит вам вернуть несколько элементов из рендера.
  • Порталы. Пусть вы выводите детей в другое поддерево DOM.
  • Booleans или null. Ничего не рендерит (В основном существует для поддержки шаблона return test && <Child />, где test является boolean.)

Функция render() должна быть чистой, то есть она не изменяет состояние компонента, возвращает один и тот же результат при каждом вызове и не взаимодействует напрямую с браузером. Если вам нужно взаимодействовать с браузером, вместо этого выполняйте свою работу в componentDidMount() или других методах жизненного цикла. Сохранение render() в чистоте - это облегчит вам и не только понимание компонентов.

render() не будет вызываться, если shouldComponentUpdate() возвращает false.

componentDidMount()

Вызывается после рендеринга компонента. Здесь можно выполнять запросы к удаленным ресурсам. В данный момент у нас есть возможность использовать refs, а следовательно это то самое место, где мы хотели бы указать установку фокуса. Так же, таймауты, ajax-запросы и взаимодействие с другими библиотеками стоит обрабатывать здесь. Этот метод жиненного цикла будет вызван лишь раз во всем цикле данного компонента и будет сигнализировать, что компонент и все его дочерние компоненты отрисовались без ошибок.

РЕКОМЕНДУЕТСЯ не делать:

  • Не вызывайте this.setState т.к. это вызовет перерисовку!

    Исключение из правила выше это апдейт состояния, которое базируется на каких-либо DOM свойствах, которые могут вычислены только после того, как компонент перерисовался (например позиция/размеры каких-либо DOM узлов). Но будьте внимательны и предотвращайте повторное обновление если значение фактически не изменилось, т.к. это может привести к циклической перерисовке.

componentWillUnmount()

Вызывается перед удалением компонента из DOM componentWillUnmount() вызывается непосредственно перед размонтированием и уничтожением компонента. Выполните любую необходимую очистку в этом методе, такую как аннулирование таймеров, отмена сетевых запросов или очистка любых подписок, созданных в componentDidMount(). Вы не должны вызывать setState() в componentWillUnmount(), потому что компонент никогда не будет перерисован. Когда экземпляр компонента размонтирован, он никогда не будет снова подключен.

Остановить метод жизненного цикла componentWillUnmount невозможно!

Дополнительные медоты жизненного цикла

shouldComponentUpdate()

Этот метод жизненного цикла отвечает на вопрос - должен ли компонент обновиться?

По умолчанию, все компоненты будут перерисовывать себя всякий раз, когда их состояние state изменяется, изменяется context или они принимают props от родителя. Если перерисовка компонента довольно тяжелая (например генерация чарта, графика) или не рекомендуется по каким-либо перфоманс причинам, то у разработчиков есть доступ к специальной функции, которая будет вызываться всякий раз при апдейт цикле.

shouldComponentUpdate будет вызываться с следующими параметрами (nextProps, nextState, nextContext) И разработчик может использовать эти параметры для того чтобы решить нужно ли делать перерисовку компонента или вернуть false и предотвратить ее. В противном случае от вас ожидают, что вы вернете true.

shouldComponentUpdate(nextProps, nextState, nextContext) {
if(nextProps.id !== this.props.id) {
return false
}
return true
}

shouldComponentUpdate всегда должен возвращать true или false!

Этот метод существует только для оптимизации производительности.

Не полагайтесь на него, чтобы «предотвратить» рендеринг, так как это может привести к ошибкам.

В настоящее время, если shouldComponentUpdate() возвращает false, то методы такие как - UNSAFE_componentWillUpdate(), render() и componentDidUpdate() вызываться не будут. В будущем React может обрабатывать shouldComponentUpdate() как подсказку, а не как строгую директиву, и возвращение false может по-прежнему приводить к повторной визуализации компонента.

РЕКОМЕНДУЕТСЯ не делать:

  • Не выполняйте никаких сайд-эффектов (Вызовы AJAX и т.д.)
  • Не вызывайте this.setState

componentWillUpdate()

Вызывается перед обновлением компонента (если shouldComponentUpdate возвращает true)

Если мы не реализовали функцию shouldComponentUpdate или же решили, что компонент должен обновиться в этом рендер цикле, вызовется другая функция жизненного цикла. Эта функция в основном используется для того чтобы сделать синхронизацию между состоянием (state) и props в случае если часть состояния компонента базируется на каких-либо props.

В случаях когда shouldComponentUpdate реализована, функция componentWillUpdate может быть использована вместо componentWillReceiveProps, т.к. она будет вызываться только тогда, когда компонент действительно будет перерисован.

Подобно всем другим componentWill* функциям, эта функция может быть вызывана несколько раз перед render, поэтому не рекомендуется выполнять здесь никакие операции вызывающие сайд-эффекты.

В данный момент этот метод жизненно цикла уже устарел и был устранен(deprecated)

Вы можете использовать старый componentWillUpdate только с приставкоый UNSAFE_componentWillUpdate()

getSnapshotBeforeUpdate()

Новый метод жизненного цикла добавленный в React 16.3

getSnapshotBeforeUpdate() вызывается непосредственно перед тем, как последний обработанный вывод будет зафиксирован, например, в. ДОМ. Это позволяет вашему компоненту захватывать некоторую информацию из DOM (например, положение прокрутки) до того, как она потенциально может быть изменена. Любое значение, возвращаемое этим жизненным циклом, будет передано в качестве параметра componentDidUpdate().

Этот вариант использования не распространен, но он может возникать в пользовательских интерфейсах, таких как цепочка чата, которым необходимо обрабатывать положение прокрутки особым образом.

class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
// Добавляем ли мы новые элементы в список?
// Захват позиции прокрутки, чтобы мы могли настроить прокрутку позже.
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// Если у вас есть значение моментального снимка, мы только что добавили новые элементы.
// Отрегулируйте прокрутку, чтобы эти новые элементы не выталкивали старые из поля зрения.
// (snapshot here is the value returned from getSnapshotBeforeUpdate)
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return <div ref={this.listRef}>{/* ...contents... */}</div>;
}
}

В вышеприведенном примере важно прочитать свойство scrollHeight в getSnapshotBeforeUpdate, потому что могут быть задержки между жизненными циклами фазы «рендеринга» (например, рендеринга) и фазами «фиксации» (такими как getSnapshotBeforeUpdate и componentDidUpdate).

  • getSnapshotBeforeUpdate() должен возвращать объект, чтобы обновить состояние, или null, чтобы ничего не обновлять.
  • getSnapshotBeforeUpdate() используется в паре с componentDidUpdate() без него React выдаст ошибку
  • в качетве параметров получает (prevProps, prevState, prevContext)

componentDidUpdate()

Вызывается сразу после обновления компонента (если shouldComponentUpdate возвращает true). В качестве параметров передаются старые значения объектов props,state, context.

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

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

Как componentWillUpdate и componentWillRecieveProps в эту функцию передается предыдущие props, состояние state и контекст context, даже если в этих значениях не было изменений. Поэтому разработчики должны вручную проверять переданные значения на изменения и только потом производить различные апдейт операции.

componentDidUpdate(prevProps, prevState, prevContext) {
if(prevProps.myProps !== this.props.myProp) {
// У this.props.myProp изменилось значение
// Поэтому мы можем выполнять любые операции для которых
// нужны новые значения и/или выполнять сайд-эффекты
// вроде AJAX вызовов с новым значением - this.props.myProp
// this.setState({...})
}
}

Вы можете вызвать setState() немедленно в componentDidUpdate(), но учтите, что он должен быть заключен в условие, как в примере выше, иначе вы вызовете бесконечный цикл. Это также вызвало бы дополнительный повторный рендеринг, который, хотя и невидим для пользователя, может повлиять на производительность компонента. Если вы пытаетесь «отразить» какое-то состояние для props, идущих сверху, рассмотрите возможность использования props напрямую.

Если ваш компонент реализует жизненный цикл getSnapshotBeforeUpdate() (что встречается редко), возвращаемое им значение будет передано в качестве дополнительного параметра «снимка» в componentDidUpdate(). В противном случае этот параметр будет неопределенным.

Почитать 📚

Практика 👩‍💻👨‍💻

class Resizer extends React.Component {
state = {
width: 0,
};
componentDidMount() {
window.addEventListener("resize", this.onResize);
}
componentWillUnmount() {
window.removeEventListener("resize", this.onResize);
}
onResize = (e) => {
this.setState({ width: e.target.innerWidth });
};
render() {
return <div>Current width: {this.state.width}</div>;
}
}

1 FakeLoading

Комопнент должен выглядит похожим образом: https://codesandbox.io/s/zndmg (Fake Loading)

Комопнент должен принимать следующие props

  • delay - кол-во мс через которое должно отобразиться success message
  • success message - сообщение которое должно отображаться после завершения загрузки

2 Notification

Комопнент должен принимать следующие props

  • delay - кол-во мс сколько должен отображаться нотификейшн
  • message - сообщение которое должно отображаться

Комопнент должен выглядит похожим образом: https://zndmg.csb.app/ (Notification)

Дополнительно:

  • Добавить возможность укаазывать сообщение для нотификации через input

3 StopWatch

Комопнент должен выглядит похожим образом: https://zndmg.csb.app/ (З StopWatch)

Дополнительно:

  • Кнопка старт должна быть disable если секундомер отсчитывает время
  • Сделаать так, что бы секнудомер показывал отчет 20s или 1m 20s или 1h 22m 30s

4 Задание Resizer

Используя за основу компонент Resizer реализовать Resizer

Комопнент должен выглядит похожим образом: https://zndmg.csb.app/

Дополнительно

  • добавить debounce
Edit this page on GitHub