Жизненный цикл компонента — 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="" onClick=this.handleClick>>name>a>br /> div>description>div> div> ); > >
Но не все задачи решаются так просто. Представьте себе компонент , имитирующий цифровые часы в формате чч:мм:сс. Заготовка:
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> ); > >
Компонент по-прежнему показывает текущее время на момент отрисовки/перерисовки компонента, но теперь он готов к изменению. Время относится к периодическим событиям, для которых используются таймеры. Для подойдет setInterval . Таймер должен быть установлен сразу после отрисовки часов и должен быть очищен при удалении компонента из дерева элементов.
setInterval(() => this.setState( date: new Date() >), 1000);
Где запускать таймер? render вызывается на каждое изменение состояния, а значит он не подходит. Ведь тогда будет запускать новый таймер каждую секунду. Конструктор кажется более подходящим местом, но здесь ожидает сюрприз. Вызов конструктора и отрисовка часов в 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.
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
React.Component
Эта страница содержит подробный справочник API для определения классового компонента React. Предполагается, что вы знакомы с такими концепциями React, как компоненты и пропсы, а также состояние и жизненный цикл. Прочитайте про них, если вы этого не сделали.
React позволяет определять компоненты как классы или функции. В настоящее время классовые компоненты имеют больше возможностей. Они разобраны на этой странице. Чтобы определить такой компонент, необходимо отнаследоваться от React.Component :
class Welcome extends React.Component render() return h1>Привет, this.props.name>h1>; > >
Единственный обязательный метод в подклассе React.Component — render() . Все остальные методы, описанные ниже, являются необязательными.
Мы рекомендуем не создавать собственные классы базовых компонентов. В компонентах React повторное использование кода обычно достигается за счёт композиции, а не наследования.
Примечание:
React не заставляет вас использовать синтаксис классов из ES6. Вместо этого вы можете использовать модуль create-react-class или его аналоги. Посмотрите раздел Использование React без ES6, чтобы узнать больше.
Жизненный цикл компонента
Каждый компонент имеет несколько «методов жизненного цикла». Переопределение такого метода позволяет выполнять код на конкретном этапе этого процесса. Вы можете использовать эту диаграмму жизненного цикла как шпаргалку. Далее на странице полужирным шрифтом выделены самые распространённые методы жизненного цикла.
При создании экземпляра компонента и его вставке в DOM, следующие методы вызываются в установленном порядке:
Обновление происходит при изменении пропсов или состояния. Следующие методы вызываются в установленном порядке при повторном рендере компонента:
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
- UNSAFE_componentWillUpdate()
- UNSAFE_componentWillReceiveProps()
Этот метод вызывается при удалении компонента из DOM:
Следующие методы вызываются, если произошла ошибка в процессе рендеринга, методе жизненного цикла или конструкторе любого дочернего компонента.
- static getDerivedStateFromError()
- componentDidCatch()
Другие методы API
В каждом компоненте доступны методы API:
Распространённые методы жизненного цикла
Методы в этом разделе охватывают большинство задач, с которыми вы столкнётесь при использовании React-компонентов. Для визуального представления вы можете использовать эту диаграмму жизненного цикла.
render()
render() — единственный обязательный метод в классовом компоненте.
При вызове он проверяет this.props и this.state и возвращает один из следующих вариантов:
- Элемент React. Обычно создаётся с помощью JSX. Указывает React, что рендерить: DOM-узел или пользовательский компонент. Например, или .
- Массивы и фрагменты. Возвращает несколько элементов из render() . Подробнее про фрагменты.
- Порталы. Рендерит несколько дочерних элементов в другое поддерево DOM. Подробнее про порталы.
- Строки и числа. Рендерит текстовые DOM-узлы.
- Booleans или null . Ничего не рендерит. (Обычно необходим для поддержки паттерна return test && , где test — логическое значение.)
Функция render() должна быть чистой. Это означает, что она не изменяет состояние компонента, всегда возвращает один и тот же результат, не взаимодействует напрямую с браузером.
Взаимодействовать с браузером необходимо в componentDidMount() или других методах жизненного цикла. Чистый render() делает компонент понятным.
constructor(props)
Вы можете не использовать конструктор в React-компоненте, если вы не определяете состояние или не привязываете методы.
Конструктор компонента React вызывается до того, как компонент будет примонтирован. В начале конструктора необходимо вызывать super(props) . Если это не сделать, this.props не будет определён. Это может привести к багам.
Конструкторы в React обычно используют для двух целей:
- Инициализация внутреннего состояния через присвоение объекта this.state .
- Привязка обработчиков событий к экземпляру.
Вы не должны вызывать setState() в constructor() . Если вам нужно внутреннее состояние, присвойте начальное состояние this.state прямо в конструкторе.
constructor(props) super(props); // Не вызывайте здесь this.setState()! this.state = counter: 0 >; this.handleClick = this.handleClick.bind(this); >
Конструктор — единственное место, где можно напрямую изменять this.state . В остальных методах необходимо использовать this.setState() .
Не используйте побочные эффекты или подписки в конструкторе. Вместо этого используйте componentDidMount() .
Примечание:
Не копируйте пропсы в состояние! Это распространённая ошибка:
constructor(props) super(props); // Не делайте этого! this.state = color: props.color >; >
Проблема в том, что это излишне и приводит к багам (обновления в пропе color не будут зафиксированы в состоянии). Вместо этого используйте this.props.color .
Используйте данный подход, если вы намеренно хотите игнорировать обновления пропсов. В таком случае лучше переименовать проп в initialColor или defaultColor . После этого вы сможете заставить компонент «сбросить» своё внутреннее состояние, изменив свой ключ, когда это будет необходимо.
Прочитайте нашу статью в блоге про отсутствие необходимости в производном состоянии. Она описывает случаи, в которых вам необходимо состояние, зависящее от пропсов.
componentDidMount()
componentDidMount() вызывается сразу после монтирования (то есть, вставки компонента в DOM). В этом методе должны происходить действия, которые требуют наличия DOM-узлов. Это хорошее место для создания сетевых запросов.
Этот метод подходит для настройки подписок. Но не забудьте отписаться от них в componentWillUnmount() .
Вы можете сразу вызвать setState() в componentDidMount() . Это вызовет дополнительный рендер перед тем, как браузер обновит экран. Гарантируется, что пользователь не увидит промежуточное состояние, даже если render() будет вызываться дважды. Используйте этот подход с осторожностью, он может вызвать проблемы с производительностью. В большинстве случаев начальное состояние лучше объявить в constructor() . Однако, это может быть необходимо для случаев, когда нужно измерить размер или положение DOM-узла, на основе которого происходит рендер. Например, для модальных окон или всплывающих подсказок.
componentDidUpdate(prevProps, prevState, snapshot)
componentDidUpdate() вызывается сразу после обновления. Не вызывается при первом рендере.
Метод позволяет работать с DOM при обновлении компонента. Также он подходит для выполнения таких сетевых запросов, которые выполняются на основании результата сравнения текущих пропсов с предыдущими. Если пропсы не изменились, новый запрос может и не требоваться.
componentDidUpdate(prevProps) // Популярный пример (не забудьте сравнить пропсы): if (this.props.userID !== prevProps.userID) this.fetchData(this.props.userID); > >
В componentDidUpdate() можно вызывать setState() , однако его необходимо обернуть в условие, как в примере выше, чтобы не возник бесконечный цикл. Вызов setState() влечет за собой дополнительный рендер, который незаметен для пользователя, но может повлиять на производительность компонента. Вместо «отражения» пропсов в состоянии рекомендуется использовать пропсы напрямую. Подробнее о том, почему копирование пропсов в состояние вызывает баги.
В тех редких случаях когда реализован метод жизненного цикла getSnapshotBeforeUpdate() , его результат передаётся componentDidUpdate() в качестве третьего параметра snapshot .
Примечание:
componentDidUpdate() не вызывается, если shouldComponentUpdate() возвращает false .
componentWillUnmount()
componentWillUnmount() вызывается непосредственно перед размонтированием и удалением компонента. В этом методе выполняется необходимый сброс: отмена таймеров, сетевых запросов и подписок, созданных в componentDidMount() .
Не используйте setState() в componentWillUnmount() , так как компонент никогда не рендерится повторно. После того, как экземпляр компонента будет размонтирован, он никогда не будет примонтирован снова.
Редко используемые методы жизненного цикла
Методы из этого раздела используются редко. В большинстве компонентов они не нужны, хотя иногда бывают полезны. Вы можете увидеть большинство приведённых ниже методов на этой диаграмме жизненного цикла, если наверху страницы нажмёте на чекбокс «Показать менее популярные методы жизненного цикла».
shouldComponentUpdate(nextProps, nextState)
Используйте shouldComponentUpdate() , чтобы указать необходимость следующего рендера на основе изменений состояния и пропсов. По умолчанию происходит повторный рендер при любом изменении состояния. В большинстве случаев вы должны полагаться на это поведение.
shouldComponentUpdate() вызывается перед рендером, когда получает новые пропсы или состояние. Значение по умолчанию равно true . Этот метод не вызывается при первом рендере или когда используется forceUpdate() .
Этот метод нужен только для повышения производительности. Но не опирайтесь на его возможность «предотвратить» рендер, это может привести к багам. Вместо этого используйте PureComponent , который позволяет не описывать поведение shouldComponentUpdate() вручную. PureComponent поверхностно сравнивает пропсы и состояние и позволяет не пропустить необходимое обновление.
Если вы уверены, что хотите написать его вручную, вы можете сравнить this.props с nextProps , а this.state с nextState . Верните false чтобы пропустить обновление React. Возврат false не предотвращает повторный рендер дочерних компонентов при изменении их состояния.
Мы не рекомендуем делать глубокое сравнение или использовать JSON.stringify() в shouldComponentUpdate() . Это неэффективно и плохо влияет на производительность.
В настоящее время, если shouldComponentUpdate() возвращает false , то UNSAFE_componentWillUpdate() , render() и componentDidUpdate() не будут вызваны. В будущем React может рассматривать shouldComponentUpdate() как подсказку, а не строгое указание. В таком случае возврат false сможет привести к повторному рендеру компонента.
static getDerivedStateFromProps(props, state)
getDerivedStateFromProps вызывается непосредственно перед вызовом метода render , как при начальном монтировании, так и при последующих обновлениях. Он должен вернуть объект для обновления состояния или null , чтобы ничего не обновлять.
Этот метод существует для редких случаев, когда состояние зависит от изменений в пропсах. Например, это подойдёт для реализации компонента , который сравнивает свои предыдущие и следующие дочерние компоненты, чтобы решить, какой из них нужно анимировать.
Производное состояние приводит к сложному коду и делает ваши компоненты сложными для понимания. Убедитесь, что вы знакомы с простыми альтернативами:
- Чтобы выполнить побочный эффект при изменении пропсов (например, сетевой запрос или анимацию) используйте componentDidUpdate .
- Чтобы повторно вычислить данные при изменении пропсов, используйте функцию мемоизации.
- Чтобы «сбросить» некоторое состояние при изменении пропсов, используйте управляемые компоненты или неуправляемые компоненты с ключом.
Этот метод не имеет доступа к экземпляру компонента. Чтобы использовать пропсы, состояние и методы класса в getDerivedStateFromProps() , их нужно вынести за пределы класса в виде чистых функций.
Если вы хотите повторно использовать код между getDerivedStateFromProps() и другими методами класса, извлеките чистые функции пропсов и состояния компонента и поместите их вне определения класса.
Обратите внимание, что этот метод запускается при каждом рендере, независимо от причины. Это отличается от метода UNSAFE_componentWillReceiveProps , который запускается только при повторном рендере родительского компонента, а не в результате вызова setState .
getSnapshotBeforeUpdate(prevProps, prevState)
getSnapshotBeforeUpdate() вызывается прямо перед этапом «фиксирования» (например, перед добавлением в DOM). Он позволяет вашему компоненту брать некоторую информацию из DOM (например, положение прокрутки) перед её возможным изменением. Любое значение, возвращаемое этим методом жизненного цикла, будет передано как параметр componentDidUpdate() .
Это применяется редко, но может быть полезно в таких интерфейсах, как цепочка сообщений в чатах, в которых позиция прокрутки обрабатывается особым образом.
Значение снимка (или null ) должно быть возвращено.
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) передан, значит элементы добавлены. // Выравниваем прокрутку так, чтобы новые элементы не выталкивали старые. // (снимок – значение, переданное из getSnapshotBeforeUpdate) if (snapshot !== null) const list = this.listRef.current; list.scrollTop = list.scrollHeight - snapshot; > > render() return ( div ref=this.listRef>>/* . contents. */>div> ); > >
В примерах выше важно получить значение свойства scrollHeight в getSnapshotBeforeUpdate из-за того, что могут возникать задержки между этапами жизненного цикла «рендер» (например, render ) и «фиксирование» (например, getSnapshotBeforeUpdate и componentDidUpdate ).
Предохранители — это React-компоненты, которые перехватывают JavaScript-ошибки в любом месте их дочернего дерева компонентов. Затем логируют эти ошибки и отображают запасной интерфейс вместо «поломанного» дерева компонентов. Предохранители отлавливают ошибки при рендере, в методах жизненного цикла и в конструкторах всего дерева под ними.
Классовый компонент становится предохранителем, если в нём используются методы жизненного цикла static getDerivedStateFromError() и (или) componentDidCatch() . Обновление состояния в этом методе жизненного цикла позволяет перехватить необработанную JavaScript-ошибку в дереве ниже и отобразить запасной интерфейс.
Используйте предохранители только для обработки неожиданных исключений, не используйте их для управления потоком исполнения в вашем приложении.
Примечание:
Предохранители перехватывают ошибки в компонентах ниже по дереву. Предохранители не могут поймать ошибку внутри себя.
static getDerivedStateFromError(error)
Этот метод жизненного цикла вызывается после возникновения ошибки у компонента-потомка. Он получает ошибку в качестве параметра и возвращает значение для обновления состояния.
class ErrorBoundary extends React.Component constructor(props) super(props); this.state = hasError: false >; > static getDerivedStateFromError(error) // Обновите состояние так, чтобы следующий рендер показал запасной интерфейс. return hasError: true >; > render() if (this.state.hasError) // Здесь можно рендерить запасной интерфейс return h1>Что-то пошло не так.h1>; > return this.props.children; > >
Примечание:
getDerivedStateFromError() вызывается во время этапа «рендера». Поэтому здесь запрещены любые побочные эффекты, но их можно использовать в componentDidCatch() .
componentDidCatch(error, info)
Этот метод жизненного цикла вызывается после возникновения ошибки у компонента-потомка. Он получает два параметра:
- error — перехваченная ошибка
- info — объект с ключом componentStack , содержащий информацию о компоненте, в котором произошла ошибка.
componentDidCatch() вызывается во время этапа «фиксации», поэтому здесь можно использовать побочные эффекты. Метод можно использовать для логирования ошибок.
class ErrorBoundary extends React.Component constructor(props) super(props); this.state = hasError: false >; > static getDerivedStateFromError(error) // Обновите состояние так, чтобы следующий рендер показал запасной интерфейс. return hasError: true >; > componentDidCatch(error, info) // Пример "componentStack": // in ComponentThatThrows (created by App) // in ErrorBoundary (created by App) // in div (created by App) // in App logComponentStackToMyService(info.componentStack); > render() if (this.state.hasError) // Здесь можно рендерить запасной интерфейс return h1>Что-то пошло не так.h1>; > return this.props.children; > >
Обработка ошибок в методе componentDidCatch() отличается между React-сборками для продакшена и разработки.
В процессе разработки ошибки будут подниматься (всплывать) наверх до объекта window , поэтому любой вызов window.onerror или window.addEventListener(‘error’, callback) перехватит ошибки, которые были обработаны componentDidCatch() .
На продакшене, напротив, ошибки не всплывают, поэтому родительский обработчик ошибок перехватит только те ошибки, которые не были обработаны componentDidCatch() .
Примечание:
В случае ошибки вы можете рендерить запасной интерфейс с помощью componentDidCatch() , вызвав setState . Однако, этот способ скоро будет считаться устаревшим. Используйте static getDerivedStateFromError() для рендера резервного интерфейса.
Устаревшие методы жизненного цикла
Приведённые ниже методы жизненного цикла устарели. Их не рекомендуется использовать в новом коде, хотя они продолжают работать. Вы можете узнать больше о переходе с устаревших методов жизненного цикла в блоге.
UNSAFE_componentWillMount()
Примечание:
Этот метод жизненного цикла раньше назывался componentWillMount . По этому названию он будет доступен до 17 версии. Чтобы автоматически обновить компоненты, используйте rename-unsafe-lifecycles .
UNSAFE_componentWillMount() вызывается непосредственно перед монтированием. Он вызывается перед render() , поэтому синхронный вызов setState() в этом методе не вызовет дополнительный рендер. Для инициализации состояния мы рекомендуем использовать constructor() .
Избегайте добавления каких-либо побочных эффектов или подписок в этом методе. Вместо этого используйте componentDidMount() .
Это единственный метод жизненного цикла, вызываемый при серверном рендеринге.
UNSAFE_componentWillReceiveProps(nextProps)
Примечание:
Этот метод жизненного цикла раньше назывался componentWillReceiveProps . По этому названию он будет доступен до 17 версии. Чтобы автоматически обновить компоненты, используйте rename-unsafe-lifecycles .
- Чтобы выполнить побочный эффект при изменении пропсов (например, сетевой запрос или анимацию) используйте componentDidUpdate .
- Чтобы повторно вычислить данные при изменении пропсов вместо componentWillReceiveProps используйте функцию мемоизации.
- Чтобы «сбросить» некоторое состояние при изменении пропсов вместо componentWillReceiveProps используйте управляемые компоненты или неуправляемые компоненты с ключом.
UNSAFE_componentWillReceiveProps() вызывается до того, как смонтированный компонент получит новые пропсы. Чтобы обновить состояние в ответ на изменение пропсов (например, для его сброса), можно сравнить this.props с nextProps и обновить состояние в этом методе с помощью this.setState() .
Обратите внимание, если родительский компонент заставляет ваш компонент повторно рендериться, метод будет вызываться, даже если пропсы не изменились. Убедитесь, что сравниваете текущие и следующие значения, если вы хотите обрабатывать изменения.
Во время монтирования React не вызывает UNSAFE_componentWillReceiveProps() с начальными значениями пропсов. Этот метод вызывается, если некоторые пропсы компонента могут обновиться. Вызов this.setState() обычно не вызывает UNSAFE_componentWillReceiveProps() .
UNSAFE_componentWillUpdate(nextProps, nextState)
Примечание:
Этот метод жизненного цикла раньше назывался componentWillUpdate . По этому названию он будет доступен до 17 версии. Чтобы автоматически обновить компоненты, используйте rename-unsafe-lifecycles .
UNSAFE_componentWillUpdate() вызывается непосредственно перед рендером при получении новых пропсов или состояния. В этом методе можно выполнить некоторую подготовку перед обновлением. Этот метод не вызывается при первом рендере.
Внутри этого метода нельзя вызвать this.setState() , а также делать какие-либо действия, которые влияют на обновление компонента перед возвратом UNSAFE_componentWillUpdate() (например, отправка действия Redux).
Обычно этот метод можно заменить на componentDidUpdate() . Вы можете использовать getSnapshotBeforeUpdate() для работы с DOM (например, запоминать положение прокрутки страницы).
Примечание:
UNSAFE_componentWillUpdate() не вызывается, если shouldComponentUpdate() возвращает false .
Другие методы API
В отличие от методов жизненного цикла, представленных выше (React вызывает их сам), методы, приведённые ниже, можно вызывать из компонентов.
Их всего два: setState() и forceUpdate() .
setState(updater[, callback])
setState() добавляет в очередь изменения в состоянии компонента. Также он указывает React, что компонент и его дочерние элементы должны быть повторно отрендерены с обновлённым состоянием. Этот метод используется для обновления интерфейса в ответ на обработчики событий и ответы сервера.
Думайте о setState() , как о запросе, а не как о команде немедленного обновления компонента. Для увеличения производительности React может задержать его выполнение, а затем обновить несколько компонентов за один проход. In the rare case that you need to force the DOM update to be applied synchronously, you may wrap it in flushSync , but this may hurt performance.
Метод setState() не всегда обновляет компонент сразу. Он может группировать или откладывать обновление до следующего раза. Это делает чтение this.state сразу после вызова setState() потенциальной ловушкой. Вместо этого используйте componentDidUpdate() или колбэк setState() ( setState(updater, callback) ), каждый из которых гарантированно вызывается после того как было применено обновление. Если вам нужно обновить состояние на основе предыдущего, используйте аргумент updater , описанный ниже.
setState() всегда приводит к повторному рендеру, если только shouldComponentUpdate() не возвращает false . Если используются мутабельные объекты, и условие рендеринга не может быть реализовано в shouldComponentUpdate() , вызывайте setState() только при разнице следующего и предыдущего состояния. Это предотвратит ненужные повторные рендеры.
Первым аргументом передаётся функция updater , которая имеет следующий вид:
(state, props) => stateChange
state — ссылка на состояние компонента при изменении. Объект состояния не должен мутировать. Изменения должны проявляться в виде нового объекта на основе входных данных из state и props . Предположим, что мы хотели бы увеличить значение состояния с помощью props.step :
this.setState((state, props) => return counter: state.counter + props.step>; >);
Как state , так и props , полученные функцией обновления, гарантированно будут обновлены. Результат функции поверхностно объединяется с state .
Второй параметр в setState() — необязательный колбэк, вызываемый после выполнения setState и повторного рендера компонента. Вместо этого в большинстве случаев для такой логики мы рекомендуем использовать componentDidUpdate() .
В качестве первого аргумента setState() , вместо функции, вы можете передать объект:
setState(stateChange[, callback])
В нём образуется новое состояние после поверхностного объединения с stateChange . Например, установим количество товаров в корзине:
this.setState(quantity: 2>)
Эта форма записи setState() также асинхронна, и несколько вызовов в течение одного цикла могут быть объединены вместе. Например, вам нужно увеличить количество элементов несколько раз в одном цикле. Результат этого можно представить так:
Object.assign( previousState, quantity: state.quantity + 1>, quantity: state.quantity + 1>, . )
Последующие вызовы будут переопределять значения из предыдущих вызовов того же цикла. Из-за этого количество увеличится только один раз. В случае, если следующее состояние зависит от текущего, мы рекомендуем использовать форму функции обновления:
this.setState((state) => return quantity: state.quantity + 1>; >);
Для более подробной информации смотрите:
- Руководство по состоянию и жизненному циклу
- Продвинутый уровень: когда и почему группируются вызовы setState() ?
- Продвинутый уровень: почему this.state не обновляется сразу?
component.forceUpdate(callback)
По умолчанию при изменении состояния компонента или пропсов, происходит повторный рендер. Если ваш метод render() зависит от некоторых других данных, вы можете указать React необходимость в повторном рендере, вызвав forceUpdate() .
Вызов forceUpdate() приведёт к выполнению метода render() в компоненте, пропуская shouldComponentUpdate() . Это вызовет обычные методы жизненного цикла для дочерних компонентов, включая shouldComponentUpdate() каждого дочернего компонента. React по-прежнему будет обновлять DOM только в случае изменения разметки.
Чаще всего, forceUpdate() не используется. Вместо этого используются в render() данные из this.props и this.state .
defaultProps можно определить как свойство самого класса компонента, которое позволяет установить пропсы класса по умолчанию. Это используется для неопределённых ( undefined ) пропсов, но не для пропсов со значением null . Например:
class CustomButton extends React.Component // . > CustomButton.defaultProps = color: 'синий' >;
Если props.color не передаётся, по умолчанию установится ‘синий’ :
render() return CustomButton /> ; // props.color будет установлен в 'синий' >
Если props.color имеет значение null , оно останется null :
render() return CustomButton color=null> /> ; // props.color останется null >
Строка displayName используется в сообщениях для отладки. Обычно вам не нужно её явно указывать. По умолчанию используется имя функции или класса, указанное при определении компонента. Если вам нужно установить его явно, например, для отладки или создания компонента высшего порядка, посмотрите раздел Обёртка отображаемого имени для простой отладки.
this.props содержит свойства, которые были определены тем, кто вызывает этот компонент. Подробнее об этом можно узнать в разделе Компоненты и пропсы
Существует специальный проп this.props.children , который обычно определяется дочерними тегами в JSX-выражении, а не в самом теге.
Состояние содержит данные, специфичные для этого компонента. Они могут измениться со временем. Состояние определяется пользователем и должно быть простым объектом JavaScript.
Вам не нужно вставлять в состояние значение, если оно не используется для рендера или потока данных (например, идентификатор таймера). Такие значения можно определить как поля экземпляра компонента.
Дополнительную информацию можно найти в разделе Состояние и жизненный цикл.
Никогда не мутируйте this.state напрямую, так как более поздний вызов setState() может перезаписать эту мутацию. Относитесь к this.state как к иммутабельному объекту.
Состояние и жизненный цикл
На этой странице представлены понятия «состояние» (state) и «жизненный цикл» (lifecycle) React-компонентов. Подробный справочник API компонентов находится по этой ссылке.
В качестве примера рассмотрим идущие часы из предыдущего раздела. В главе Рендеринг элементов мы научились обновлять UI только одним способом — вызовом root.render() :
const root = ReactDOM.createRoot(document.getElementById('root')); function tick() const element = ( div> h1>Привет, мир!h1> h2>Сейчас new Date().toLocaleTimeString()>.h2> div> ); root.render(element);> setInterval(tick, 1000);
В этой главе мы узнаем, как инкапсулировать и обеспечить многократное использование компонента Clock . Компонент самостоятельно установит свой собственный таймер и будет обновляться раз в секунду.
Для начала, извлечём компонент, показывающий время:
const root = ReactDOM.createRoot(document.getElementById('root')); function Clock(props) return ( div> h1>Привет, мир!h1> h2>Сейчас props.date.toLocaleTimeString()>.h2> div> ); > function tick() root.render(Clock date=new Date()> />);> setInterval(tick, 1000);
Проблема в том, что компонент Clock не обновляет себя каждую секунду автоматически. Хотелось бы спрятать логику, управляющую таймером, внутри самого компонента Clock .
В идеале мы бы хотели реализовать Clock таким образом, чтобы компонент сам себя обновлял:
root.render(Clock />);
Для этого добавим так называемое «состояние» (state) в компонент Clock .
«Состояние» очень похоже на уже знакомые нам пропсы, отличие в том, что состояние контролируется и доступно только конкретному компоненту.
Преобразование функционального компонента в классовый
Давайте преобразуем функциональный компонент Clock в классовый компонент за 5 шагов:
- Создаём ES6-класс с таким же именем, указываем React.Component в качестве родительского класса
- Добавим в класс пустой метод render()
- Перенесём тело функции в метод render()
- Заменим props на this.props в теле render()
- Удалим оставшееся пустое объявление функции
class Clock extends React.Component render() return ( div> h1>Привет, мир!h1> h2>Сейчас this.props.date.toLocaleTimeString()>.h2> div> ); > >
Теперь Clock определён как класс, а не функция.
Метод render будет вызываться каждый раз, когда происходит обновление. Так как мы рендерим в один и тот же DOM-контейнер, мы используем единственный экземпляр класса Clock — поэтому мы можем задействовать внутреннее состояние и методы жизненного цикла.
Добавим внутреннее состояние в класс
Переместим date из пропсов в состояние в три этапа:
- Заменим this.props.date на this.state.date в методе render() :
class Clock extends React.Component render() return ( div> h1>Привет, мир!h1> h2>Сейчас this.state.date.toLocaleTimeString()>.h2> div> ); > >
- Добавим конструктор класса, в котором укажем начальное состояние в переменной this.state :
class Clock extends React.Component constructor(props) super(props); this.state = date: new Date()>; > render() return ( div> h1>Привет, мир!h1> h2>Сейчас this.state.date.toLocaleTimeString()>.h2> div> ); > >
Обратите внимание, что мы передаём props базовому (родительскому) конструктору:
constructor(props) super(props); this.state = date: new Date()>; >
Классовые компоненты всегда должны вызывать базовый конструктор с аргументом props .
- Удалим проп date из элемента :
root.render(Clock />);
Позже мы вернём код таймера обратно и на этот раз поместим его в сам компонент.
Результат выглядит следующим образом:
class Clock extends React.Component constructor(props) super(props); this.state = date: new Date()>; > render() return ( div> h1>Привет, мир!h1> h2>Сейчас this.state.date.toLocaleTimeString()>.h2> div> ); > > const root = ReactDOM.createRoot(document.getElementById('root')); root.render(Clock />);
Теперь осталось только установить собственный таймер внутри Clock и обновлять компонент каждую секунду.
Добавим методы жизненного цикла в класс
В приложениях со множеством компонентов очень важно освобождать используемые системные ресурсы, когда компоненты удаляются.
Первоначальный рендеринг компонента в DOM называется «монтирование» (mounting). Нам нужно устанавливать таймер всякий раз, когда это происходит.
Каждый раз когда DOM-узел, созданный компонентом, удаляется, происходит «размонтирование» (unmounting). Чтобы избежать утечки ресурсов, мы будем сбрасывать таймер при каждом «размонтировании».
Объявим специальные методы, которые компонент будет вызывать при монтировании и размонтировании:
class Clock extends React.Component constructor(props) super(props); this.state = date: new Date()>; > componentDidMount() > componentWillUnmount() > render() return ( div> h1>Привет, мир!h1> h2>Сейчас this.state.date.toLocaleTimeString()>.h2> div> ); > >
Эти методы называются «методами жизненного цикла» (lifecycle methods).
Метод componentDidMount() запускается после того, как компонент отрендерился в DOM — здесь мы и установим таймер:
componentDidMount() this.timerID = setInterval( () => this.tick(), 1000 ); >
Обратите внимание, что мы сохраняем ID таймера в this ( this.timerID ).
Поля this.props и this.state в классах — особенные, и их устанавливает сам React. Вы можете вручную добавить новые поля, если компоненту нужно хранить дополнительную информацию (например, ID таймера).
Теперь нам осталось сбросить таймер в методе жизненного цикла componentWillUnmount() :
componentWillUnmount() clearInterval(this.timerID); >
Наконец, реализуем метод tick() . Он запускается таймером каждую секунду и вызывает this.setState() .
this.setState() планирует обновление внутреннего состояния компонента:
class Clock extends React.Component constructor(props) super(props); this.state = date: new Date()>; > componentDidMount() this.timerID = setInterval( () => this.tick(), 1000 ); > componentWillUnmount() clearInterval(this.timerID); > tick() this.setState( date: new Date() >); > render() return ( div> h1>Привет, мир!h1> h2>Сейчас this.state.date.toLocaleTimeString()>.h2> div> ); > > const root = ReactDOM.createRoot(document.getElementById('root')); root.render(Clock />);
Теперь часы обновляются каждую секунду.
Давайте рассмотрим наше решение и разберём порядок, в котором вызываются методы:
- Когда мы передаём в root.render() , React вызывает конструктор компонента. Clock должен отображать текущее время, поэтому мы задаём начальное состояние this.state объектом с текущим временем. Позже мы обновим это состояние.
- React вызывает метод render() компонента Clock . Таким образом React узнаёт, что отобразить на экране. Далее React обновляет DOM так, чтобы он соответствовал выводу рендера Clock .
- Как только вывод рендера Clock вставлен в DOM, React вызывает метод жизненного цикла componentDidMount() . Внутри него компонент Clock указывает браузеру установить таймер, который будет вызывать tick() раз в секунду.
- Таймер вызывает tick() ежесекундно. Внутри tick() мы просим React обновить состояние компонента, вызывая setState() с текущим временем. React реагирует на изменение состояния и снова запускает render() . На этот раз this.state.date в методе render() содержит новое значение, поэтому React заменит DOM. Таким образом компонент Clock каждую секунду обновляет UI.
- Если компонент Clock когда-либо удалится из DOM, React вызовет метод жизненного цикла componentWillUnmount() и сбросит таймер.
Как правильно использовать состояние
Важно знать три детали о правильном применении setState() .
Не изменяйте состояние напрямую
В следующем примере повторного рендера не происходит:
// Неправильно this.state.comment = 'Привет';
Вместо этого используйте setState() :
// Правильно this.setState(comment: 'Привет'>);
Конструктор — это единственное место, где вы можете присвоить значение this.state напрямую.
Обновления состояния могут быть асинхронными
React может сгруппировать несколько вызовов setState() в одно обновление для улучшения производительности.
Поскольку this.props и this.state могут обновляться асинхронно, вы не должны полагаться на их текущее значение для вычисления следующего состояния.
Например, следующий код может не обновить счётчик:
// Неправильно this.setState( counter: this.state.counter + this.props.increment, >);
Правильно будет использовать второй вариант вызова setState() , который принимает функцию, а не объект. Эта функция получит предыдущее состояние в качестве первого аргумента и значения пропсов непосредственно во время обновления в качестве второго аргумента:
// Правильно this.setState((state, props) => ( counter: state.counter + props.increment >));
В данном примере мы использовали стрелочную функцию, но можно использовать и обычные функции:
// Правильно this.setState(function(state, props) return counter: state.counter + props.increment >; >);
Обновления состояния объединяются
Когда мы вызываем setState() , React объединит аргумент (новое состояние) c текущим состоянием.
Например, состояние может состоять из нескольких независимых полей:
constructor(props) super(props); this.state = posts: [], comments: [] >; >
Их можно обновлять по отдельности с помощью отдельных вызовов setState() :
componentDidMount() fetchPosts().then(response => this.setState( posts: response.posts >); >); fetchComments().then(response => this.setState( comments: response.comments >); >); >
Состояния объединяются поверхностно, поэтому вызов this.setState() оставляет this.state.posts нетронутым, но полностью заменяет this.state.comments .
Однонаправленный поток данных
В иерархии компонентов ни родительский, ни дочерние компоненты не знают, задано ли состояние другого компонента. Также не важно, как был создан определённый компонент — с помощью функции или с помощью класса.
Состояние часто называют «локальным», «внутренним» или инкапсулированным. Оно доступно только для самого компонента и скрыто от других.
Компонент может передать своё состояние вниз по дереву в виде пропсов дочерних компонентов:
FormattedDate date=this.state.date> />
Компонент FormattedDate получает date через пропсы, но он не знает, откуда они взялись изначально — из состояния Clock , пропсов Clock или просто JavaScript-выражения:
function FormattedDate(props) return h2>Сейчас props.date.toLocaleTimeString()>.h2>; >
Это, в общем, называется «нисходящим» («top-down») или «однонаправленным» («unidirectional») потоком данных. Состояние всегда принадлежит определённому компоненту, а любые производные этого состояния могут влиять только на компоненты, находящиеся «ниже» в дереве компонентов.
Если представить иерархию компонентов как водопад пропсов, то состояние каждого компонента похоже на дополнительный источник, который сливается с водопадом в произвольной точке, но также течёт вниз.
Чтобы показать, что все компоненты действительно изолированы, создадим компонент App , который рендерит три компонента :
function App() return ( div> Clock /> Clock /> Clock /> div> ); >
У каждого компонента Clock есть собственное состояние таймера, которое обновляется независимо от других компонентов.
В React-приложениях, имеет ли компонент состояние или нет — это внутренняя деталь реализации компонента, которая может меняться со временем. Можно использовать компоненты без состояния в компонентах с состоянием, и наоборот.
Понимание жизненного цикла React-компонента
React предоставляет разработчикам множество методов и «хуков», которые вызываются во время жизненного цикла компонента, они позволяют нам обновлять UI и состояние приложения. Когда необходимо использовать каждый из них, что необходимо делать и в каких методах, а от чего лучше отказаться, является ключевым моментом к пониманию как работать с React.
Update:
В React 16.3 появились два дополнительных метода жизненного цикла и несколько методов объявлено устаревшими, подробности по ссылке.
(Прим. переводчика: Хотя некоторые методы и объявлены устаревшими, их описание, на мой взгляд, все равно будет полезно, хотя бы тем разработчикам, которые работают с предыдущими версиями React и в целом для понимания вместо каких методов и для чего были введены новые. Статью по ссылке добавил ниже)
Constructor:
Конструкторы являются основной ООП – это такая специальная функция, которая будет вызываться всякий раз, когда создается новый объект. Очень важно вызывать функцию super в случаях, когда наш класс расширяет поведение другого класса, который имеет конструктор. Выполнение этой специальной функции будет вызывать конструктор нашего родительского класса и разрешать ему проинициализировать себя. Вот почему мы имеем доступ к this.props только после вызова super. (имеется ввиду вызов super(props) в классе-наследнике React.Component)
Поэтому конструкторы — это отличное место для инициализации компонента – создание любых полей (переменные начинающиеся с this.) или инициализации состояния компонента на основе полученных props.
Это также единственное место где вы можете изменять/устанавливать состояние (state) напрямую перезаписывая поле this.state. Во всех других случаях необходимо использовать this.setState.
- Устанавливайте изначальное состояние компонента
- Если не используется class properties синтаксис – подготовьте все поля класса и вызовете bind на тех функциях, что будут переданы как коллбеки.
- Не выполняйте никаких сайд-эффектов (side effects) (Вызовы AJAX и т.д.)
componentWillMount не очень отличается от конструктора – она также вызывается лишь раз в изначальном жизненном цикле. Вообще исторически были некоторые причины использовать componentWillMount поверх конструктора — смотри react-redux issue, но на данный момент практика, описанная там, устарела.
У многих возможно появится соблазн использовать эту функцию для отправки реквеста на получение данных и они будут ожидать, что данные будут доступны прежде чем отработает изначальный render. Но это не тот случай – хотя реквест и будет инициализирован перед render, он не успеет выполниться прежде чем render будет вызван.
Кроме того, с изменениями в React Fiber (после релиза React 16 beta) эта функция может быть вызвана несколько раз перед вызовом изначального render, что может привести к различным побочным эффектам, связанным с этим. Поэтому, не рекомендуется использовать эту функцию для выполнения любых операций вызывающих сайд-эффекты.
Также важно отметить, что эта функция вызывается, когда используется рендеринг на стороне сервера (server side rendering), когда ее антипод – componentDidMount не будет вызван на сервере, но будет на клиенте. Поэтому если некоторый сайд-эффект нацелен на серверную часть, эта функция может быть использована как исключение.
И наконец функция setState может быть свободно использована и не будет вызывать перерисовку компонента.
- Обновляйте состояние через this.setState
- Выполняйте последние оптимизации
- Вызывайте сайд-эффекты (Вызов AJAX и т.д.) только в случае server-side-rendering.
- Не выполняйте никаких сайд-эффектов (Вызов AJAX и т.д.) на стороне клиента.
Эта функция будет вызываться при каждом апдейт жизненном цикле, который будет происходить при изменениях в props (когда перерисовывается родительский компонент) и будет принимать маппинг всех передаваемых props, не важно изменялось значение какого-либо свойства или нет с предыдущей фазы перерисовки.
Эта функция будет идеальна, если у вас есть какой-нибудь компонент, часть состояния которого (state), зависит от props передаваемых от родительского компонента, т.к. вызов this.setState здесь не будет приводить к дополнительной перерисовке.
Запомните, что т.к. эта функция вызывается со всеми props, даже с теми что не менялись, от разработчика ждут, что он напишет проверку, чтобы понять поменялось ли актуальное значение какого-либо свойства или нет.
componentWillReceiveProps(nextProps) < if(nextProps.myProp !== this.props.myProps) < // nextProps.myProp имеет другое значение, чем наше текущее myProps // поэтому мы можем что-нибудь рассчитать базируясь на новом значении. >>
В связи с тем, что в React Fiber (после 16 beta) эта функция может вызываться несколько раз перед функцией render, не рекомендуется выполнять здесь никакие операции вызывающие сайд-эффекты.
- Синхронизируйте состояние (state) с props
- Не выполняйте никаких сайд-эффектов (Вызовы AJAX и т.д.)
По умолчанию, все компоненты будут перерисовывать себя всякий раз, когда их состояние (state) изменяется, изменяется контекст или они принимают props от родителя. Если перерисовка компонента довольно тяжелая (например генерация чарта, графика) или не рекомендуется по каким-либо перфоманс причинам, то у разработчиков есть доступ к специальной функции, которая будет вызываться всякий раз при апдейт цикле.
Эта функция будет вызываться с следующими значениями props, состоянием (state) и объектом. И разработчик может использовать эти параметры для того чтобы решить нужно ли делать перерисовку компонента или вернуть false и предотвратить ее. В противном случае от вас ожидают, что вы вернете true.
- Используйте для оптимизации производительности компонента
- Не выполняйте никаких сайд-эффектов (Вызовы AJAX и т.д.)
- Не вызывайте this.setState
Если мы не реализовали функцию shouldComponentUpdate или же решили, что компонент должен обновиться в этом рендер цикле, вызовется другая функция жизненного цикла. Эта функция в основном используется для того чтобы сделать синхронизацию между состоянием (state) и props в случае если часть состояния компонента базируется на каких-либо props.
В случаях когда shouldComponentUpdate реализована, функция componentWillUpdate может быть использована вместо componentWillReceiveProps, т.к. она будет вызываться только тогда, когда компонент действительно будет перерисован.
Подобно всем другим componentWill* функциям, эта функция может быть вызывана несколько раз перед render, поэтому не рекомендуется выполнять здесь никакие операции вызывающие сайд-эффекты.
- Синхронизируйте состояние (state) с props
- Не выполняйте никаких сайд-эффектов (Вызовы AJAX и т.д.)
Эта функция будет вызываться после того как отработала функция render, в каждом цикле перерисовки. Это означает, что вы можете быть уверены, что компонент и все его дочерние компоненты уже перерисовали себя.
В связи с этим эта функция является единственной функцией, что гарантировано будет вызвана только раз в каждом цикле перерисовки, поэтому любые сайд-эффекты рекомендуется выполнять именно здесь. Как componentWillUpdate и componentWillRecieveProps в эту функцию передается предыдущие props, состояние (state) и контекст, даже если в этих значениях не было изменений. Поэтому разработчики должны вручную проверять переданные значения на изменения и только потом производить различные апдейт операции.
componentDidUpdate(prevProps) < if(prevProps.myProps !== this.props.myProp) < // У this.props.myProp изменилось значение // Поэтому мы можем выполнять любые операции для которых // нужны новые значения и/или выполнять сайд-эффекты // вроде AJAX вызовов с новым значением - this.props.myProp >>
- Выполняйте сайд-эффекты (Вызовы AJAX и т.д.)
- Не вызывайте this.setState т.к. это будет вызывать циклическую перерисовку.
componentDidCatch(errorString, errorInfo)
Дополнение в React 16 – этот метод жизненного цикла является особым, т.к. он позволяет реагировать на события, происходящие в дочернем компоненте, а конкретно на любые неперехваченные ошибки в любом из дочерних компонентов.
С помощью этого дополнения вы можете сделать ваш родительский элемент обработчиком ошибок. Например – писать информацию об ошибке в состояние компонента, возвращать соответствующее сообщение в рендер, или делать логирование ошибки.
componentDidCatch(errorString, errorInfo) < this.setState(< error: errorString >); ErrorLoggingTool.log(errorInfo); > render() < if(this.state.error) return /> return ( // render normal component output ); >
Когда происходит какая-либо ошибка, эта функция вызывается с следующими параметрами:
- errorString — .toString() сообщение о ошибке
- errorInfo – объект с одним полем componentStack, которое содержит стектрейс, где произошла ошибка.
in Thrower in div (created by App) in App
componentDidMount
Эта функция будет вызвана лишь раз во всем жизненном цикле данного компонента и будет сигнализировать, что компонент и все его дочерние компоненты отрисовались без ошибок.
Т.к. эта функция гарантирована будет вызвана лишь раз, то это превосходный кандидат для выполнения любых сайд-эффектов, как то AJAX запросы.
- Выполняйте сайд-эффекты (Вызовы AJAX и т.д.)
- Не вызывайте this.setState т.к. это вызовет перерисовку.
componentWillUnmount
Используйте эту функцию для «очистки» после компонента, если он использует таймеры (setTimeout, setInterval), открывает сокеты или производит любые операции, которые нуждаются в закрытии или удалении.
- Удаляйте таймеры и слушателей (listeners) созданных во время жизни компонента.
- Не вызывайте this.setState, не стартуйте новых слушателей или таймеры.
Циклы компонента
Есть несколько причин по которым компонент может перерисовываться, и в зависимости от причины вызываются различные функции, позволяющие разработчику выполнять обновление определенных частей компонента.
Создание компонента
Первый цикл это создание компонента, которое обычно происходит при первом обнаружении компонента в распарсенном JSX дереве:
Компонент перерисовывается в связи с перерисовкой родительского компонента
Компонент перерисовывается в связи с внутренними изменениями (например вызов this.setState())
Компонент перерисовывается в связи с вызовом this.forceUpdate
Компонент перерисовывается в связи с перехватом ошибки
Введено в React 16 как ErrorBoundaries. Компонент может определять специальный слой, который может перехватывать ошибки и предоставлять новый метод жизненного цикла – componentDidCatch – который дает разработчику возможность обработать или залогировать эти ошибки.
@James_k_nelson — недавно опубликовал componentWillRecieveProps симулятор. ТУТ вы можете найти и поиграться с этим симулятором.
React 16.3+ жизненный цикл компонента
Релиз 16.3 привнес некоторые новые функции жизненного цикла, которые заменили существующие чтобы предоставить лучшую поддержку новой асинхронной природы React.
static getDerivedStateFromProps(nextProps, prevState)
Основная ответственность этой новой функции — это убедиться, что состояние (state) и props синхронизированы, когда это необходимо. Ее основной смысл — это замена componentWillRecieveProps.
getDerivedStateFromProps – это статическая функция и поэтому не имеет доступа к this – вместо этого от вас ожидают, что вы вернете объект, который будет смержен в будущее состояние компонента (в точности как работа с setState!)
Эта функция используется, когда компонент обновляется, но также и когда он монтируется, сразу после вызова конструктора, поэтому вам больше не нужно использовать конструктор если вы хотите установить начальное состояние компонента из props.
getSnapshotBeforeUpdate(prevProps, prevState)
Другая из двух новых функций, вызывается в так называемой “pre-commit фазе”, прямо перед изменениями из VDOM, которые должны быть отображены в DOM.
Ее можно использовать в основном, если вам нужно прочитать текущее состояние DOM.
Например у вас есть приложение, в котором новые сообщения добавляются сверху экрана – если пользователь будет скроллить вниз, и добавится новое сообщение, экран будет «прыгать» и это сделает UI тяжелее в использовании. Добавлением getSnapshotBeforeUpdate вы сможете рассчитать текущее положение скролла и восстанавливать его через апдейт DOM-а.
Хотя функция не является статической, рекомендуется возвращать значение, а не апдейтить компонент. Возвращаемое значение будет передано в componentDidUpdate как 3-й параметр.
Устаревшие функции
Несмотря на то, что новые функции делают ваш переход на AsyncMode легче, вас не заставляют мигрировать весь ваш код. Следующие функции будут маркированы как устаревшие и в следующих релизах переименованы:
- componentWillRecieveProps – UNSAFE_componentWillRecieveProps
- componentWillUpdate – UNSAFE_componentWillUpdate
Dan Abramov суммировал все изменения в одной картинке: