Рендер-пропсы
Термин «рендер-проп» относится к возможности компонентов React разделять код между собой с помощью пропа, значение которого является функцией.
Компонент с рендер-пропом берёт функцию, которая возвращает React-элемент, и вызывает её вместо реализации собственного рендера.
Такой подход, в частности, применяется в библиотеках React Router, Downshift и Formik.
В этой статье мы покажем, чем полезны и как писать рендер-пропсы.
Использование рендер-пропа для сквозных задач
Компоненты — это основа повторного использования кода в React. Однако бывает неочевидно, как сделать, чтобы одни компоненты разделяли своё инкапсулированное состояние или поведение с другими компонентами, заинтересованными в таком же состоянии или поведении.
Например, следующий компонент отслеживает положение мыши в приложении:
Когда курсор перемещается по экрану, компонент отображает координаты (x, y) внутри
Возникает вопрос: как мы можем повторно использовать это поведение в другом компоненте? То есть если другому компоненту необходимо знать о позиции курсора, можем ли мы как-то инкапсулировать это поведение, чтобы затем легко использовать его в этом компоненте?
Теперь компонент инкапсулирует всё поведение, связанное с обработкой событий mousemove и хранением позиций курсора (x, y), но пока не обеспечивает повторного использования.
Для начала вы можете отрендерить внутри метода render компонента следующим образом:
Этот подход будет работать для конкретного случая, но мы не достигли основной цели — инкапсулировать поведение с возможностью повторного использования. Теперь, каждый раз когда мы хотим получить позицию мыши для разных случаев, нам требуется создавать новый компонент (т. е. другой экземпляр ), который рендерит что-то специально для этого случая.
Вот здесь рендер-проп нам и понадобится: вместо явного указания внутри компонента, и трудозатратных изменений на выводе рендера, мы предоставляем функцию в качестве пропа, с которой мы используем динамическое определение того, что нужно передавать в рендер-проп.
Иными словами, рендер-проп — функция, которая сообщает компоненту что необходимо рендерить.
Эта техника позволяет сделать легко портируемым поведение, которое мы хотим повторно использовать. Для этого следует отрендерить компонент с помощью рендер-пропа, который сообщит, где отрендерить курсор с текущим положением (x, y).
Таким образом, рендер-пропы позволяют реализовать любой из описанных выше паттернов.
Использование пропсов, отличных от render (как название передаваемого свойства)
И запомните, проп children не обязательно именовать в списке «атрибутов» вашего JSX-элемента. Вместо этого, вы можете поместить его прямо внутрь элемента!
Эту технику можно увидеть в действии в API библиотеки react-motion.
Поскольку этот метод не совсем обычен, вы, вероятно, захотите явно указать, что children должен быть функцией в вашем propTypes при разработке такого API.
Будьте осторожны при использовании рендер-проп вместе с React.PureComponent
Чтобы решить эту проблему, вы можете определить проп как метод экземпляра, например так:
Компоненты и пропсы¶
Компоненты позволяют разбить интерфейс на независимые части, про которые легко думать в отдельности. Их можно складывать вместе и использовать несколько раз. На этой странице мы ознакомимся с самой идеей компонентов.
Во многом компоненты ведут себя как обычные функции JavaScript. Они принимают произвольные входные данные (так называемые «пропсы») и возвращают React-элементы, описывающие, что мы хотим увидеть на экране.
Функциональные и классовые компоненты¶
Проще всего объявить React-компонент как функцию:
Эта функция — компонент, потому что она получает данные в одном объекте («пропсы») в качестве параметра и возвращает React-элемент. Мы будем называть такие компоненты «функциональными», так как они буквально являются функциями.
Ещё компоненты можно определять как классы ES6:
С точки зрения React, эти два компонента эквивалентны.
Классам доступны дополнительные возможности, о которых мы поговорим в следующих главах. Но пока что мы предпочтём функции за их краткость.
Как отрендерить компонент¶
Пока что мы только встречали React-элементы, представляющие собой DOM-теги:
Но элементы могут описывать и наши собственные компоненты:
Когда React встречает подобный элемент, он собирает все JSX-атрибуты в один объект и передаёт их нашему компоненту. Этот объект называется «пропсы» (props).
Например, этот компонент выведет «Привет, Алиса» на страницу:
Давайте разберём, что именно здесь происходит:
Привет, Алиса
Привет, Алиса
Примечание: Всегда называйте компоненты с заглавной буквы.
Если компонент начинается с маленькой буквы, React принимает его за DOM-тег. Например,
Чтобы узнать больше про это соглашение, прочитайте Углублённое изучение JSX.
Композиция компонентов¶
Компоненты могут ссылаться на другие компоненты в возвращённом ими дереве. Это позволяет нам использовать одну и ту же абстракцию — компоненты — на любом уровне нашего приложения. Неважно, пишем ли мы кнопку, форму или целый экран: все они, как правило, представляют собой компоненты в React-приложениях.
Например, компонент App может отрендерить компонент Welcome несколько раз:
Извлечение компонентов¶
Не бойтесь разбивать компоненты на части.
Допустим, у нас есть компонент Comment :
Этот компонент представляет собой комментарий в социальной сети. Его пропсы включают в себя author (объект), text (строка), и date (дата).
С этим компонентом может быть не очень удобно работать из-за излишней вложенности. Мы также не можем повторно использовать его составные части. Давайте извлечём из него пару компонентов.
Для начала извлечём Avatar :
Пропсы следует называть так, чтобы они имели смысл в первую очередь с точки зрения самого компонента, а уже во вторую тех компонентов, которые его рендерят.
Теперь можно немножко упростить наш Comment :
Это позволит ещё сильнее упростить Comment :
Пропсы можно только читать¶
Компонент никогда не должен что-то записывать в свои пропсы — вне зависимости от того, функциональный он или классовый.
Возьмём для примера функцию sum :
Такие функции называют «чистыми», потому что они не меняют свои входные данные и предсказуемо возвращают один и тот же результат для одинаковых аргументов.
А вот пример нечистой функции — она записывает данные в свои же аргументы:
React достаточно гибкий, но есть одно правило, которое нельзя нарушать:
React-компоненты обязаны вести себя как чистые функции по отношению к своим пропсам.
Конечно, интерфейсы приложений обычно изменяются с течением времени. В следующей главе мы узнаем о том, что такое «состояние» компонента. Состояние даёт компонентам возможность реагировать на действия пользователя, ответы сервера и другие события, не нарушая чистоту компонента.
2.5 Компоненты и свойства
Компоненты позволяют разделить UI на независимые, повторно используемые части и работать с каждой из них отдельно.
Концептуально, компоненты похожи на JavaScript-функции. Они принимают произвольные данные (называемые props) и возвращают React-элементы, которые описывают то, что должно появиться на экране.
2.5.1 Компоненты-функции и компоненты-классы
Самый простой способ объявить компонент – это написать JavaScript-функцию:
Эта функция является корректным React-компонентом, потому что она принимает единственный объект props с данными в качестве аргумента и возвращает React-элемент. Такие компоненты называются «функциональными», так как они и есть JavaScript-функции.
Компонент можно объявить другим способом. Для этого нужно использовать ES6-класс:
Два приведенных выше компонента являются эквивалентными с точки зрения React. Но пока мы будем использовать функциональные компоненты, так как они короче.
2.5.2 Отрисовка компонентов
Ранее, мы наталкивались лишь на React-элементы, которые представляли собой DOM-теги:
Тем не менее, элементы могут быть представлены пользовательскими(кастомными) компонентами:
Например, этот код отрисовывает на странице «Hello, Sara»:
Давайте прорезюмируем то, что произошло в этом примере:
Hello, Sara
Hello, Sara
Всегда именуйте свои компоненты с большой буквы.
Например,
2.5.3 Композиция компонентов
Компоненты могут ссылаться на другие компоненты в своём выводе (результате отрисовки). Это позволяет нам использовать ту же самую абстракцию компонента для любого уровня детализации. Кнопка, форма, диалог, экран: в React-приложении все эти сущности выражены компонентами.
Как правило, новые React-приложения имеют единственный компонент App на самом верху иерархии. Тем не менее, если вы интегрируете React в уже существующее приложение, вы можете начать снизу вверх с маленького компонента, такого как Button и постепенно двигаться вверх по иерархии отображения.
2.5.4 Извлечение компонентов
Не бойтесь разделять компоненты на более мелкие компоненты.
Рассмотрим пример с компонентом Comment :
Он принимает author (объект), text (строка) и date (дата) как свойства, и описывает комментарий на социальном веб-сайте.
Данный компонент довольно сложно изменить из-за его вложенности. Также тяжело повторно использовать и его составные части. Давайте извлечем из него несколько небольших компонентов, упростив исходный компонент.
Для начала давайте извлечем из него компонент Avatar :
Мы рекомендуем именовать props с точки зрения компонента, а не контекста, в котором он будет использован.
Сейчас мы можем немного упростить компонент Comment :
Это позволяет нам еще больше упростить компонент Comment :
Извлечение компонентов по началу может показаться рутинной работой. Однако набор универсальных, переиспользуемых, отлаженных компонентов с лихвой окупит все усилия в больших приложениях, экономя массу времени.
2.5.4 Свойства props – только для чтения
Такие функции называются «чистыми». Потому что они не изменяют свои аргументы и всегда возвращают одинаковый результат для одних и тех же аргументов.
В противоположность им, следующая функция не является чистой, потому что она изменяет свои аргументы:
React является очень гибким, но он имеет одно строгое правило:
Конечно, UI приложения – динамический и изменяется со временем. В следующем разделе мы познакомимся с новой концепцией «состояния». Состояние позволяет React-компонентам изменять их вывод со временем в ответ на действия пользователя, ответы сети или что-то другое, не нарушая данное правило.
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
Авторизуйтесь
Способы передачи данных между компонентами в React
В React есть несколько способов передачи данных между компонентами: Render props / props, Context, React-Redux / Redux.
В этой статье вы узнаете об этих способах подробно. В каждом из них вы научитесь добиваться двух вариантов взаимодействия:
Render props / props
Один из самых простых способов передачи данных в компоненты — это props (свойства).
Что такое prop?
Как известно, разметка в компонентах рендерится так:
Это отобразится как C component
C фактически отображается в P, так как P является его родительским компонентом. Кроме того, P может отобразить C следующим образом:
P добавляет дополнительные свойства в тег элемента C. Это очень похоже на
В случае выше будет создан HTMLDivElement :
Имя атрибута будет установлено в объект props вот так:
и передастся классу компонента C с помощью React.
React основан на JSX, поэтому разметка в P будет скомпилирована в таком виде:
Первый параметр — это элемент для рендеринга, а второй содержит атрибуты элемента (в виде объектного литерала). CreateElement возвращает литерал, содержащий первый и второй параметры.
Старт 22 ноября, 4 месяца, Онлайн, От 35 000 до 100 000 ₽
При рендеринге React проверяет, является ли этот литерал на самом деле элементом или же классом. Если это класс, React создаёт его экземпляр, используя ключевое слово new, затем передает этот экземпляр в объект props и вызывает метод render.
Приведённый выше код показывает, что фактически делает React для рендеринга компонента или элемента.
Примечание: фрагмент выше не полный, он нужен, чтобы продемонстрировать, как компоненты получают аргумент props.
Таким образом P удаётся отправить данные в C.
Но как C теперь может отправлять данные обратно в родительский класс P? Это делается с помощью render props.
Что такое render props? Это концепция, посредством которой функция может быть передана дочерним компонентам в качестве свойства (props). Строка, объект, число или массив могут быть переданы через props дочерним компонентам. Это реализовано следующим образом:
Здесь функция () =>
Итак, теперь вы знаете, что такое render props (или свойства для рендеринга). Давайте посмотрим, как дочерние компоненты могут использовать их для связи с родительскими.
Что происходит в этом фрагменте? У компонента P метод вывода связан с его экземпляром. bind(this) говорит JS запустить функцию внутри области видимости класса, объекта или функции. Вывод здесь выполняется за пределами класса P, но он по-прежнему может ссылаться на все свойства и методы, определённые в P.
Таким образом, метод вывода передаётся в компонент класса C, чтобы получить аргумент props через свойство func.
Теперь C успешно связался со своим родителем P. Можно изменить код, чтобы C отправлял данные в P.
Как вы видите, значение P отправилось из его дочернего элемента C.
Props и render props — это одно и то же, но они имеют разные концепции. Render props в основном используется для разделения логики рендеринга между компонентами. Но в данном случае этот метод был использован для передачи данных от дочернего компонента к родительскому.
Context
Для большинства разработчиков передача props глубоко вложенным компонентам в дереве была бы утомительной. React предоставляет способ определять глобальные данные в одном месте и получать к ним доступ из любого места в приложении.
Context (контекст) как раз используется в React для обмена данными между глубоко вложенными компонентами.
Чтобы сообщить React о намерении передать context от родительского компонента остальным его дочерним элементам, нужно определить два атрибута в родительском классе:
Родительский компонент определяет context, который React использует для передачи данных глобально своим дочерним элементам.
Как теперь сделать так, чтобы дочерние элементы могли передавать данные своим родителям?
Дочерние компоненты могут изменять значение context, который будет отражаться во всех вложенных компонентах, включая и родительский компонент.
React-Redux/Redux
Это своего рода “ремейк” способа Context в React. Redux — это простая библиотека управления свойствами приложения, которая позволяет легко хранить эти свойства в одном месте и изменять их из любого места.
React-Redux был создан, чтобы сделать Redux легко совместимым с приложениями React.
Поскольку состояние определяется в одном месте, родительские компоненты могут устанавливать или обновлять его значение, а дочерние могут это фиксировать: связь Родитель-Наследник.
Аналогично дочерние компоненты могут устанавливать или обновлять значение состояния в хранилище, и оно также будет фиксироваться родительскими компонентами: связь Наследник-Родитель.
Заключение
В этом материале вы увидели, как происходит взаимодействие между компонентами React и какими способами можно этого достичь:
В первом разделе вы увидели использование props и render props, они работают с помощью HTML-подобных атрибутов. Context использует центральное хранилище, которое родитель и потомки могут как читать, так и обновлять. React-Redux и Redux делают то же самое, что и Context, компоненты выполняют чтение/запись из центрального хранилища.
Для дальнейшего освоения React вам может быть полезен данный материал.




