что такое пропсы react
Разбираемся с Render Props на примере
Привет, Хабр! Представляю вашему вниманию перевод статьи «Learn Render Props by Example».
Нам поручено создать кнопку, открывающую окно PayPal:
Мы открываем наш редактор и создаем компонент PayPalLauncher:
PayPalLauncher может содержать экземпляр PayPal, с дополнительной логикой, поэтому неплохо было бы обернуть его (PayPal) в компонент.
Но что, если требуется использовать дополнительные элементы PayPal? Например:
Теперь у нас есть две кнопки и оранжевая ссылка, которая должна открыть окно PayPal. Давайте рассмотрим несколько способов реализовать это.
Вариант 1 – Логика в render()
Вначале у вас может возникнуть соблазн написать код внутри PayPalLauncher:
Здесь мы передаем prop, называемый type для того, чтобы определить какой элемент PayPal рендерить. Много логики, чтобы выразить несколько визуальных изменений. Это все еще не дает нам возможности легко отрисовывать любой компонент по необходимости. Что если есть способ отделить логику от представления?
Вариант 2 — Вложенность
Вложенность помогает нам писать более понятный JSX.
Теперь у нас есть четкое разделение между логикой (PayPalLauncher) и представлением (PayPalButton). Это дает нам возможность отображать любой компонент в качестве элемента PayPal (PayPal trigger). И это хорошо читается! Но как мы передаем props между PayPalLauncher и PayPalButton? Давайте посмотрим как это выглядит в PayPalLauncher:
Что здесь происходит?! Мы клонируем children и предаем prop в launchPayPal неявно, каждому ребенку. Это значит, что каждый ребенок, которого вы вложите в PayPalLauncher, должен принимать prop launchPayPal. Хотя вложенность дает нам более понятный JSX, этот метод не является идеальным, особенно если мы пытаемся создать переиспользуемые компоненты с общими интерфейсами.
Вариант 3 — Render Props
Render Props — это метод передачи props от родителя ребенку, используя функцию или замыкание. Давайте посмотрим, как это выглядит:
Вместо того, чтобы обрабатывать props.children в качестве ноды, мы создаем замыкание и выбираем аргументы для передачи дочерним нодам. В этом случае мы используем метод экземпляра класса launchPayPal.
Когда мы реализуем это, полученный вариант будет выглядеть так:
Что это значит? Вместо элементов, мы передаем функцию как ребенка в PayPalLauncher. Из-за этого мы можем легко передать launchPayPal в обработчик onClick компонента PayPalButton. Теперь мы можем рендерить любой нужный нам компонент, и мапить родительский launchPayPal в любой обработчик ребенка.
Неожиданным преимуществом использования render props является то, что этот подход дает возможность передавать launchPayPal в Page, и позволяет нам добавлять больше функциональности. Например, допустим, вы хотите зарегистрировать пользователя и подтвердить форму перед запуском окна PayPal:
Поскольку launchPayPal отображается через render props на Page, мы можем легко добавить дополнительный контекстно-зависимый функционал, используя композицию функций. Здесь мы сохраняем разницу между Page и PayPalLauncher и используем pipe, чтобы улучшить читаемость.
Что мы получаем при использовании render props?
* Помните, что render props является паттерном и может быть реализован несколькими способами — только вы должны выбирать, что использовать с вашем случае.
React.Component
Эта страница содержит подробный справочник API для определения классового компонента React. Предполагается, что вы знакомы с такими концепциями React, как компоненты и пропсы, а также состояние и жизненный цикл. Прочитайте про них, если вы этого не сделали.
React позволяет определять компоненты как классы или функции. В настоящее время классовые компоненты имеют больше возможностей. Они разобраны на этой странице. Чтобы определить такой компонент, необходимо отнаследоваться от React.Component :
Мы рекомендуем не создавать собственные классы базовых компонентов. В компонентах React повторное использование кода обычно достигается за счёт композиции, а не наследования.
React не заставляет вас использовать синтаксис классов из ES6. Вместо этого вы можете использовать модуль create-react-class или его аналоги. Посмотрите раздел Использование React без ES6, чтобы узнать больше.
Жизненный цикл компонента
Каждый компонент имеет несколько «методов жизненного цикла». Переопределение такого метода позволяет выполнять код на конкретном этапе этого процесса. Вы можете использовать эту диаграмму жизненного цикла как шпаргалку. Далее на странице полужирным шрифтом выделены самые распространённые методы жизненного цикла.
При создании экземпляра компонента и его вставке в DOM, следующие методы вызываются в установленном порядке:
Этот метод устарел. Не используйте его в новом коде.
Обновление происходит при изменении пропсов или состояния. Следующие методы вызываются в установленном порядке при повторном рендере компонента:
Эти методы устарели. Не используйте их в новом коде.
Этот метод вызывается при удалении компонента из DOM:
Следующие методы вызываются, если произошла ошибка в процессе рендеринга, методе жизненного цикла или конструкторе любого дочернего компонента.
В каждом компоненте доступны методы API:
Распространённые методы жизненного цикла
Методы в этом разделе охватывают большинство задач, с которыми вы столкнётесь при использовании React-компонентов. Для визуального представления вы можете использовать эту диаграмму жизненного цикла.
render() — единственный обязательный метод в классовом компоненте.
При вызове он проверяет this.props и this.state и возвращает один из следующих вариантов:
Функция render() должна быть чистой. Это означает, что она не изменяет состояние компонента, всегда возвращает один и тот же результат, не взаимодействует напрямую с браузером.
Взаимодействовать с браузером необходимо в componentDidMount() или других методах жизненного цикла. Чистый render() делает компонент понятным.
Вы можете не использовать конструктор в React-компоненте, если вы не определяете состояние или не привязываете методы.
Конструкторы в React обычно используют для двух целей:
Не копируйте пропсы в состояние! Это распространённая ошибка:
Прочитайте нашу статью в блоге про отсутствие необходимости в производном состоянии. Она описывает случаи, в которых вам необходимо состояние, зависящее от пропсов.
componentDidMount() вызывается сразу после монтирования (то есть, вставки компонента в DOM). В этом методе должны происходить действия, которые требуют наличия DOM-узлов. Это хорошее место для создания сетевых запросов.
componentDidUpdate() вызывается сразу после обновления. Не вызывается при первом рендере.
Метод позволяет работать с DOM при обновлении компонента. Также он подходит для выполнения таких сетевых запросов, которые выполняются на основании результата сравнения текущих пропсов с предыдущими. Если пропсы не изменились, новый запрос может и не требоваться.
Редко используемые методы жизненного цикла
Методы из этого раздела используются редко. В большинстве компонентов они не нужны, хотя иногда бывают полезны. Вы можете увидеть большинство приведённых ниже методов на этой диаграмме жизненного цикла, если наверху страницы нажмёте на чекбокс «Показать менее популярные методы жизненного цикла».
Этот метод нужен только для повышения производительности. Но не опирайтесь на его возможность «предотвратить» рендер, это может привести к багам. Вместо этого используйте PureComponent , который позволяет не описывать поведение shouldComponentUpdate() вручную. PureComponent поверхностно сравнивает пропсы и состояние и позволяет не пропустить необходимое обновление.
Этот метод существует для редких случаев, когда состояние зависит от изменений в пропсах. Например, это подойдёт для реализации компонента
Производное состояние приводит к сложному коду и делает ваши компоненты сложными для понимания. Убедитесь, что вы знакомы с простыми альтернативами:
Если вы хотите повторно использовать код между getDerivedStateFromProps() и другими методами класса, извлеките чистые функции пропсов и состояния компонента и поместите их вне определения класса.
Это применяется редко, но может быть полезно в таких интерфейсах, как цепочка сообщений в чатах, в которых позиция прокрутки обрабатывается особым образом.
Значение снимка (или null ) должно быть возвращено.
В примерах выше важно получить значение свойства scrollHeight в getSnapshotBeforeUpdate из-за того, что могут возникать задержки между этапами жизненного цикла «рендер» (например, render ) и «фиксирование» (например, getSnapshotBeforeUpdate и componentDidUpdate ).
Предохранители — это React-компоненты, которые перехватывают JavaScript-ошибки в любом месте их дочернего дерева компонентов. Затем логируют эти ошибки и отображают запасной интерфейс вместо «поломанного» дерева компонентов. Предохранители отлавливают ошибки при рендере, в методах жизненного цикла и в конструкторах всего дерева под ними.
Используйте предохранители только для обработки неожиданных исключений, не используйте их для управления потоком исполнения в вашем приложении.
Предохранители перехватывают ошибки в компонентах ниже по дереву. Предохранители не могут поймать ошибку внутри себя.
Этот метод жизненного цикла вызывается после возникновения ошибки у компонента-потомка. Он получает ошибку в качестве параметра и возвращает значение для обновления состояния.
Этот метод жизненного цикла вызывается после возникновения ошибки у компонента-потомка. Он получает два параметра:
componentDidCatch() вызывается во время этапа «фиксации», поэтому здесь можно использовать побочные эффекты. Метод можно использовать для логирования ошибок.
Обработка ошибок в методе componentDidCatch() отличается между React-сборками для продакшена и разработки.
Устаревшие методы жизненного цикла
Приведённые ниже методы жизненного цикла устарели. Их не рекомендуется использовать в новом коде, хотя они продолжают работать. Вы можете узнать больше о переходе с устаревших методов жизненного цикла в блоге.
Это единственный метод жизненного цикла, вызываемый при серверном рендеринге.
Использование этого метода жизненного цикла часто приводило к багам
Обратите внимание, если родительский компонент заставляет ваш компонент повторно рендериться, метод будет вызываться, даже если пропсы не изменились. Убедитесь, что сравниваете текущие и следующие значения, если вы хотите обрабатывать изменения.
UNSAFE_componentWillUpdate() вызывается непосредственно перед рендером при получении новых пропсов или состояния. В этом методе можно выполнить некоторую подготовку перед обновлением. Этот метод не вызывается при первом рендере.
В отличие от методов жизненного цикла, представленных выше (React вызывает их сам), методы, приведённые ниже, можно вызывать из компонентов.
setState() добавляет в очередь изменения в состоянии компонента. Также он указывает React, что компонент и его дочерние элементы должны быть повторно отрендерены с обновлённым состоянием. Этот метод используется для обновления интерфейса в ответ на обработчики событий и ответы сервера.
Эта форма записи setState() также асинхронна, и несколько вызовов в течение одного цикла могут быть объединены вместе. Например, вам нужно увеличить количество элементов несколько раз в одном цикле. Результат этого можно представить так:
Последующие вызовы будут переопределять значения из предыдущих вызовов того же цикла. Из-за этого количество увеличится только один раз. В случае, если следующее состояние зависит от текущего, мы рекомендуем использовать форму функции обновления:
Для более подробной информации смотрите:
Если props.color не передаётся, по умолчанию установится ‘синий’ :
Строка displayName используется в сообщениях для отладки. Обычно вам не нужно её явно указывать. По умолчанию используется имя функции или класса, указанное при определении компонента. Если вам нужно установить его явно, например, для отладки или создания компонента высшего порядка, посмотрите раздел Обёртка отображаемого имени для простой отладки.
this.props содержит свойства, которые были определены тем, кто вызывает этот компонент. Подробнее об этом можно узнать в разделе Компоненты и пропсы
Состояние содержит данные, специфичные для этого компонента. Они могут измениться со временем. Состояние определяется пользователем и должно быть простым объектом JavaScript.
Вам не нужно вставлять в состояние значение, если оно не используется для рендера или потока данных (например, идентификатор таймера). Такие значения можно определить как поля экземпляра компонента.
Дополнительную информацию можно найти в разделе Состояние и жизненный цикл.
React.Component
Эта страница содержит подробный справочник API для определения класса компонента React. Предполагается, что вы знакомы с фундаментальными концепциями React, такими как компоненты и свойства, а также состояние и жизненный цикл. Если вы этого не сделали, сначала прочитайте их.
React позволяет вам определять компоненты как классы или функции. В настоящее время компоненты, определённые как классы, предоставляют больше возможнотей, которые подробно описаны на этой странице. Чтобы определить класс компонента React, вам необходимо расширить React.Component :
Мы настоятельно не рекомендуем создавать собственные классы базовых компонентов. В React-компонентах повторное использование кода в основном достигается с помощью композиции, а не наследования.
React не заставляет вас использовать синтаксис класса ES6. Если вы предпочитаете избегать его использования, вы можете установить пакет create-react-class или аналогичную пользовательскую абстракцию. Посмотрите на Использование React без ES6, чтобы узнать больше.
Жизненный цикл компонента
Каждый компонент имеет несколько «методов жизненного цикла», которые вы можете переопределить для выполнения кода в определенное время в процессе. Вы можете использовать эту диаграмму жизненного цикла в качестве шпаргалки. В приведённом ниже списке обычно используемые методы жизненного цикла выделены полужирным шрифтом. Остальные из них существуют для относительно редких случаев использования.
Эти методы вызывают в следующем порядке, когда экземпляр компонента создаётся и добавляется в DOM:
Эти методы считаются устаревшими, и вам следует избегать их использование в новом коде:
Обновление может быть вызвано изменениями в свойствах или состоянии. Эти методы вызываются в следующем порядке, когда компонент повторно отрисовывается:
Эти методы считаются устаревшими, и вам следует избегать их использование в новом коде:
Этот метод вызывается, когда компонент удаляется из DOM:
Этот метод вызывается при возникновении ошибки во время отрисовки, в методе жизненного цикла или в конструкторе любого дочернего компонента.
Каждый компонент также предоставляет некоторые другие методы API:
Общеиспользуемые методы жизненного цикла
Методы в этом разделе охватывают подавляющее большинство случаев использования, с которыми вы столкнётесь при создании React-компонентов. Для наглядной иллюстрации ознакомьтесь с этой диаграммой жизненного цикла.
Метод render() — единственный обязательный методов в классовом компоненте.
При вызове он должен проверять this.props и this.state и возвращать один из следующих типов:
Функция render() должна быть чистой, что означает, что она не изменяет состояние компонента, она возвращает один и тот же результат при каждом вызове и не взаимодействует напрямую с браузером.
Если вам нужно взаимодействовать с браузером, выполняйте все необходимые операции в componentDidMount() или других методах жизненного цикла. Сохранение render() чистым делает компонент легче для понимания.
Если вы не инициализируете состояние и не привязываете методы, вам не нужно реализовывать конструктор в вашем React-компоненте.
Конструктор для компонента React вызывается до того, как будет примонтирован. При реализации конструктора подкласса React.Component вы должны называть super(props) перед любым другим выражением. В противном случае this.props не будет определен в конструкторе, что может привести к багам.
Как правило, в React конструкторы используются только для двух целей:
** Избегайте копирования свойств в состояние! Это распространённая ошибка:**
componentDidMount() вызывается сразу после монтирования компонента (вставлено в DOM-дерево). Инициализация, требующая DOM-узлов, должна быть здесь. Если вам нужно загружать данные с удалённой конечной точки (endpoint), это хорошее место для создания экземпляра сетевого запроса.
componentDidUpdate() вызывается сразу после обновления. Этот метод не вызывается при первоначальной отрисовке.
Используйте данный метод как возможность работать с DOM при обновлении компонента. Это также хорошее место для выполнения сетевых запросов, если вы сравниваете текущие свойства с предыдущими свойствами (например, не нужно делать сетевой запрос, если свойство не изменилось).
Редко используемые методы жизненного цикла
Методы в этом разделе соответствуют малораспространённым случаям использования. Они удобны время от времени, но большинство ваших компонентов, вероятно, не нуждаются ни в одном из них. Вы можете увидеть большинство приведенных ниже методов на этой диаграмме жизненного цикла, если наверху страницы вы нажмете на чекбокс «Show less common lifecycles».
Вывод состояния приводит к подробному коду и затрудняет понимание ваших компонентов. Убедитесь, что вы хорошо знакомы с более простыми альтернативами:
Если вы хотите повторно вычислить некоторые данные только при изменении свойств, используйте помощник мемоизации вместо этого.
Этот метод не имеет доступа к экземпляру компонента. Если вы хотите, то можете повторно использовать код между getDerivedStateFromProps() и другими методами класса, извлекая чистые функции свойства и состояния компонента вне определения класса.
Этот не распространённый вариант использования, но он может быть в пользовательских интерфейсах, таких как цепочка сообщений в чатах, который должен обрабатывать позицию прокрутки особым образом.
Должно быть возвращено значение снимка (или null ).
Граница ошибок — это React-компоненты, которые перехватывают ошибки JavaScript в любом месте их дочернего дерева компонентов, логируют эти ошибки и отображают резервный интерфейс вместо разрушенного дерева компонентов. Граница ошибок отлавливают ошибки при отрисовке, в методах жизненного цикла и в конструкторах всего дерева под ними.
Классовый компонент становится границей ошибки, если он определяет этот метод жизненного цикла. Вызов setState() в нём позволяет зафиксировать необработанную JavaScript-ошибку в приведённом ниже дереве и отобразить резервный интерфейс. Используйте только граница ошибок для восстановления от неожиданных исключений; не пытайтесь использовать их для управления потоком.
Для получения дополнительной информации смотрите раздел Обработка ошибок в React 16.
Граница ошибок перехватывают только ошибки в компонентах ниже в их дереве. Граница ошибки не может поймать ошибку внутри себя.
This lifecycle is invoked after an error has been thrown by a descendant component. It receives the error that was thrown as a parameter and should return a value to update state.
getDerivedStateFromError() is called during the “render” phase, so side-effects are not permitted. For those use cases, use componentDidCatch() instead.
Устаревшие методы жизненного цикла
The lifecycle methods below are marked as “legacy”. They still work, but we don’t recommend using them in the new code. You can learn more about migrating away from legacy lifecycle methods in this blog post.
Это единственный хук жизненного цикла, вызываемый серверной отрисовкой.
Использование этого метода жизненного цикла часто приводит к ошибкам и несоответствиям.
UNSAFE_componentWillReceiveProps() вызывается до того, как смонтированный компонент получит новые свойства. Если вам нужно обновить состояние в ответ на изменения свойства (например, для его сброса), вы можете сравнить this.props и nextProps и выполнить переходы состояния с помощью this.setState() в этом методе.
Обратите внимание, что если родительский компонент заставляет ваш компонент повторно отрисовываться, этот метод будет вызываться, даже если свойства не изменились. Убедитесь в том, что сравниваете текущие и следующие значения, если вы только хотите обрабатывать изменения.
UNSAFE_componentWillUpdate() вызывается непосредственно перед отрисовкой при получении новых свойств или состояния. Используйте это как возможность выполнить подготовку до того, как произойдет обновление. Этот метод не вызывается при первоначальной отрисовке.
UNSAFE_componentWillUpdate() не будет вызываться, если shouldComponentUpdate() возвращает false.
В отличие от описанных выше методов жизненного цикла (которые React вызывает за вас), ниже приведены методы, которые вы можете вызывать из своих компонентов.
setState () ставит в очередь изменения в состояние компонента и указывает React, что этот компонент и его дочерние элементы должны быть повторно отрисованы с обновлённым состоянием. Это основной метод, который вы будете использовать для обновления пользовательского интерфейса в ответ на обработчики событий и ответы сервера.
Думайте о setState() как о запросе, а не как о команде немедленного действия для обновления компонента. Для лучшей очевидной производительности React может задержать выполнение, а затем обновить несколько компонентов за один проход. React не гарантирует незамедлительного применения изменений в состоянии.
Первый аргумент — это функция updater со следующим определением:
Второй параметр setState() — необязательный колбэк, вызываемый после завершения работы setState и далее компонент будет повторно отрисован. Обычно вместо этого мы рекомендуем использовать componentDidUpdate() для подобной логики.
Вы можете опционально передать объект в качестве первого аргумента setState() вместо функции:
Следующий фрагмент кода выполняет поверхностное объединение stateChange в новое состояние, например, чтобы скорректировать количество товаров в корзине:
Эта форма записи setState() также асинхронна, и несколько вызовов в течение одного цикла могут быть объединены (сгруппированы) вместе. Например, если вы пытаетесь увеличить количество элементов более одного раза в одном цикле, результат будет эквивалентным следующему ниже коду:
Последующие вызовы будут переопределять значения из предыдущих вызовов в том же самом цикле, поэтому количество будет увеличиваться только один раз. Если следующее состояние зависит от предыдущего состояния, мы рекомендуем использовать форму функции для обновления, т.е. следующим образом:
Для более подробной информации смотрите:
Если props.color не предоставлен, по умолчанию будет установлено значение ‘синий’ :
Строка displayName используется для отладочных сообщений. Обычно вам не нужно явно указывать её, поскольку по умолчанию предполагается имя функции или класса, который определяет компонент. Возможно, вы захотите установить его явно, если хотите отобразить другое имя для целей отладки или когда вы создаёте компонент высшего порядка, смотрите раздел Обтекание отображаемого имени для легкой отладки для получения подробной информации.
this.props содержит свойства, которые были определены вызывающим элементом этого компонента. Смотрите раздел Компоненты и реквизит для ознакомления со свойствами.
В частности, this.props.children — специальное свойство, обычно определяемое дочерними тегами в JSX-выражении, а не в самом теге.
Состояние содержит данные, конкретные для этого компонента, которые могут измениться со временем. Состояние определёно пользователем, и оно должно быть простым объектом JavaScript.
Если какое-либо значение не используется для отрисовки или потока данных (например, идентификатор таймера), вам не нужно вставлять его в состояние. Такие значения могут быть определены как поля экземпляра компонента.
Дополнительную информацию о состоянии смотрите в разделе Состояние и жизненный цикл.