render props react что это
Render Props
The term “render prop” refers to a technique for sharing code between React components using a prop whose value is a function.
A component with a render prop takes a function that returns a React element and calls it instead of implementing its own render logic.
Libraries that use render props include React Router, Downshift and Formik.
In this document, we’ll discuss why render props are useful, and how to write your own.
Use Render Props for Cross-Cutting Concerns
Components are the primary unit of code reuse in React, but it’s not always obvious how to share the state or behavior that one component encapsulates to other components that need that same state.
For example, the following component tracks the mouse position in a web app:
As the cursor moves around the screen, the component displays its (x, y) coordinates in a
Now the question is: How can we reuse this behavior in another component? In other words, if another component needs to know about the cursor position, can we encapsulate that behavior so that we can easily share it with that component?
Since components are the basic unit of code reuse in React, let’s try refactoring the code a bit to use a component that encapsulates the behavior we need to reuse elsewhere.
Now the component encapsulates all behavior associated with listening for mousemove events and storing the (x, y) position of the cursor, but it’s not yet truly reusable.
For example, let’s say we have a component that renders the image of a cat chasing the mouse around the screen. We might use a prop to tell the component the coordinates of the mouse so it knows where to position the image on the screen.
As a first pass, you might try rendering the inside ’s render method, like this:
This approach will work for our specific use case, but we haven’t achieved the objective of truly encapsulating the behavior in a reusable way. Now, every time we want the mouse position for a different use case, we have to create a new component (i.e. essentially another ) that renders something specifically for that use case.
Here’s where the render prop comes in: Instead of hard-coding a inside a component, and effectively changing its rendered output, we can provide with a function prop that it uses to dynamically determine what to render–a render prop.
Now, instead of effectively cloning the component and hard-coding something else in its render method to solve for a specific use case, we provide a render prop that can use to dynamically determine what it renders.
More concretely, a render prop is a function prop that a component uses to know what to render.
This technique makes the behavior that we need to share extremely portable. To get that behavior, render a with a render prop that tells it what to render with the current (x, y) of the cursor.
One interesting thing to note about render props is that you can implement most higher-order components (HOC) using a regular component with a render prop. For example, if you would prefer to have a withMouse HOC instead of a component, you could easily create one using a regular with a render prop:
So using a render prop makes it possible to use either pattern.
Using Props Other Than render
It’s important to remember that just because the pattern is called “render props” you don’t have to use a prop named render to use this pattern. In fact, any prop that is a function that a component uses to know what to render is technically a “render prop”.
And remember, the children prop doesn’t actually need to be named in the list of “attributes” in your JSX element. Instead, you can put it directly inside the element!
You’ll see this technique used in the react-motion API.
Since this technique is a little unusual, you’ll probably want to explicitly state that children should be a function in your propTypes when designing an API like this.
Be careful when using Render Props with React.PureComponent
Using a render prop can negate the advantage that comes from using React.PureComponent if you create the function inside a render method. This is because the shallow prop comparison will always return false for new props, and each render in this case will generate a new value for the render prop.
In this example, each time renders, it generates a new function as the value of the prop, thus negating the effect of extending React.PureComponent in the first place!
To get around this problem, you can sometimes define the prop as an instance method, like so:
3.12 Паттерн: свойство render
Компонент со свойством render принимает функцию, которая возвращает элемент React, и вызывает её вместо реализации своей собственной логики отрисовки.
В этом разделе мы обсудим, почему полезно использовать свойство для отрисовки.
3.12.1 Используйте «свойство render» для сквозной функциональности
Компоненты являются основным элементом повторного использования кода в React, но не всегда очевидно, как совместно использовать состояние или поведение, которые один компонент инкапсулирует в другие компоненты, нуждающиеся в этом состоянии.
Например, следующий компонент отслеживает позицию мыши в веб-приложении:
Когда курсор перемещается по экрану, компонент отображает его координаты (x, y) в
Теперь возникает вопрос: как мы можем повторно использовать это поведение в другом компоненте? Другими словами, если другой компонент должен знать о позиции курсора, можем ли мы инкапсулировать это поведение таким образом, чтобы можно было легко поделиться им с этим компонентом?
Теперь компонент инкапсулирует все поведение, связанное с прослушиванием событий mousemove и хранением позиции (x, y) курсора, но он еще не может быть использован повторно.
Такой подход будет работать для нашего конкретного случая, но мы не достигли цели по-настоящему инкапсулировать поведение для повторного использования. Теперь, каждый раз, когда нам нужна позиция мыши для какого-нибудь другого случая, мы должны создать новый компонент (т. е. по существу другой ), который отрисовывает что-то конкретное для данного случая.
Вот где в силу вступает паттерн «свойство render»: вместо жесткого кодирования внутри компонента и изменения результата его отрисовки, мы можем предоставить компоненту особое свойство-функцию, которое он использует для динамического определения того, что необходимо отрисовать. Это особое свойство и есть «свойство render».
Более конкретно: свойство render является свойством-функцией, которую компонент использует, для определения того, что ему необходимо отрисовывать.
Таким образом, использование свойства render дает возможность применить любой паттерн.
3.12.2 Использование свойств, отличных от «render»
И помните, что на самом деле нет необходимости указывать свойство children в списке «атрибутов» вашего JSX-элемента. Вместо этого вы можете поместить его прямо внутри этого элемента!
Вы можете увидеть этот подход в react-motion API.
3.12.3 Будьте внимательны, когда используете паттерн «свойство render» с React.PureComponent
Чтобы обойти эту проблему, иногда вы можете определить свойство как метод экземпляра, например:
Рендер-пропсы¶
Термин «рендер-проп» относится к возможности компонентов React разделять код между собой с помощью пропа, значение которого является функцией.
Компонент с рендер-пропом берёт функцию, которая возвращает React-элемент, и вызывает её вместо реализации собственного рендера.
Такой подход, в частности, применяется в библиотеках React Router и Downshift.
В этой статье мы покажем, чем полезны и как писать рендер-пропсы.
Использование рендер-пропа для сквозных задач¶
Компоненты — это основа повторного использования кода в React. Однако бывает неочевидно, как сделать, чтобы одни компоненты разделяли своё инкапсулированное состояние или поведение с другими компонентами, заинтересованными в таком же состоянии или поведении.
Например, следующий компонент отслеживает положение мыши в приложении:
Когда курсор перемещается по экрану, компонент отображает координаты (x, y) внутри
Возникает вопрос: как мы можем повторно использовать это поведение в другом компоненте? То есть если другому компоненту необходимо знать о позиции курсора, можем ли мы как-то инкапсулировать это поведение, чтобы затем легко использовать его в этом компоненте?
Теперь компонент инкапсулирует всё поведение, связанное с обработкой событий mousemove и хранением позиций курсора (x, y), но пока не обеспечивает повторного использования.
Для начала вы можете отрендерить внутри метода render компонента следующим образом:
Этот подход будет работать для конкретного случая, но мы не достигли основной цели — инкапсулировать поведение с возможностью повторного использования. Теперь, каждый раз когда мы хотим получить позицию мыши для разных случаев, нам требуется создавать новый компонент (т. е. другой экземпляр ), который рендерит что-то специально для этого случая.
Вот здесь рендер-проп нам и понадобится: вместо явного указания внутри компонента, и трудозатратных изменений на выводе рендера, мы предоставляем функцию в качестве пропа, с которой мы используем динамическое определение того, что нужно передавать в рендер-проп.
Иными словами, рендер-проп – функция, которая сообщает компоненту что необходимо рендерить.
Эта техника позволяет сделать легко портируемым поведение, которое мы хотим повторно использовать. Для этого следует отрендерить компонент с помощью рендер-пропа, который сообщит, где отрендерить курсор с текущим положением (x, y).
Таким образом, рендер-пропы позволяют реализовать любой из описанных выше паттернов.
Использование пропсов, отличных от render (как название передаваемого свойства)¶
И запомните, проп children не обязательно именовать в списке «атрибутов» вашего JSX-элемента. Вместо этого, вы можете поместить его прямо внутрь элемента!
Эту технику можно увидеть в действии в API библиотеки react-motion.
Поскольку этот метод не совсем обычен, вы, вероятно, захотите явно указать, что children должен быть функцией в вашем propTypes при разработке такого API.
Предостережения¶
Будьте осторожны при использовании рендер-проп вместе с React.PureComponent¶
Чтобы решить эту проблему, вы можете определить проп как метод экземпляра, например так:
Компоненты и пропсы
Компоненты позволяют разбить интерфейс на независимые части, про которые легко думать в отдельности. Их можно складывать вместе и использовать несколько раз. На этой странице мы ознакомимся с самой идеей компонентов — детальное описание API находится здесь.
Во многом компоненты ведут себя как обычные функции 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-компоненты обязаны вести себя как чистые функции по отношению к своим пропсам.
Конечно, интерфейсы приложений обычно изменяются с течением времени. В следующей главе мы узнаем о том, что такое «состояние» компонента. Состояние даёт компонентам возможность реагировать на действия пользователя, ответы сервера и другие события, не нарушая чистоту компонента.
Разбираемся с Render Props и HOC в React
Детальное представление Render Props и компонентов высшего порядка в React
Для чего нужны эти паттерны?
React предлагает Компоненты, представляющие собой простой способ повторного использования кода. Компонент инкапсулирует множество вещей от контента, стилей и до бизнес-логики. Поэтому в идеале компонент может содержать комбинацию из html, css и js, каждый из которых имеет одну цель — единственную ответственность.
Совет: при помощи Bit (GitHub) вы можете организовать коллекцию переиспользуемых компонентов и применять их совместно с другими разработчиками. Компоненты могут быть разработаны и импортированы из разных проектов и приложений. Это гораздо быстрее, чем переписывать их или же поддерживать большую библиотеку. Просто попробуйте 🙂
Пример
Предположим, что мы работаем над приложением для E-commerce. Как и в любом другом E-commerce приложении пользователь видит список всех доступных товаров, а также может добавить любой из них в корзину. Мы же получаем данные о товарах через API и показываем каталог товаров в виде карточек.
В этом случае компонент React можно реализовать подобным образом:
Для админов существует панель администратора, где они могут добавлять или удалять товары. В этой панели мы запрашиваем данные о товарах с того же API и показываем каталог товаров в форме таблицы.
React компонент может быть таким:
Единственное, что сразу же бросается в глаза — это то, что оба компонента реализуют логику получения данных о товарах.
В дальнейшем такие ситуации могут возникнуть снова.
Если мы создадим разные компоненты для каждого из этих случаев, мы получим большое количество дублированного кода.
Получение и отображение данных — это две отдельные задачи. Как было сказано ранее, гораздо лучше, когда один компонент имеет единственную ответственность.
Давайте отрефакторим наш первый компонент. Он будет получать данные товаров в качестве свойства (prop) и рендерить (метод render) каталог товаров в виде списка карточек, как мы делали до этого. Так как нам больше не нужно состояние компонента и методы жизненного цикла, мы переделаем компонент в функциональный.
Теперь он будет выглядеть так:
Здесь в игру вступают render props и HOC (компоненты высшего порядка). Они являются всего лишь способами, при помощи которых компонент узнает, что ему нужно отрендерить. Это еще больше увеличивает возможность повторного использования кода.
Теперь, когда мы знаем для чего они нужны, давайте посмотрим как их использовать.
Render Props
На концептуальном уровне понять Render Props очень просто. Давайте забудем на минуту о React и посмотрим на вещи в контексте ванильного JavaScript.
У нас есть функция, которая вычисляет сумму двух чисел. Для начала мы просто хотим вывести результат в консоль. Итак, мы написали следующую функцию:
Однако вскоре мы узнали, что функция сложения очень полезна и нужна нам также и в других местах. Поэтому вместо выведения в консоль мы хотим, чтобы эта функция только возвращала результат, а вызывающая ее функция решала как его использовать.
Это можно сделать следующим образом:
Мы передаем функции sum в качестве аргумента fn функцию обратного вызова. Затем функция sum вычисляет результат и вызывает функцию fn с этим результатом в качестве аргумента. Таким способом функция обратного вызова получает результат и может делать с ним всё, что угодно.
В этом и состоит суть render props. Картина прояснится при использовании данного паттерна на практике, поэтому давайте применим его к проблеме, с которой мы столкнулись.
В React это можно реализовать следующим образом:
Подобно аргументу fn у нас есть render prop, которому будет передана функция. Затем компонент ProductData вызывает эту функцию с данными товаров в качестве аргумента.
Следовательно, мы можем использовать компонент ProductData как показано ниже.
Как мы видим, render props — достаточно универсальный паттерн. Большинство задач могут быть выполнены довольно однообразно. И именно поэтому мы можем “выстрелить себе в ногу”.
Простой способ избежать вложенности — это разбить компонент на более мелкие и переместить их в отдельные файлы. Ещё один способ — это создать больше компонентов и собирать их в один, вместо длинных функций внутри render props.
Далее мы рассмотрим другой популярный паттерн под названием HOC.
Компоненты высшего порядка (HOC)
В рамках данного паттерна мы определяем функцию, которая принимает компонент в качестве аргумента и затем возвращает тот же самый компонент, но с дополнительной функциональностью.
Если описание кажется вам знакомым, то это потому, что данный паттерн похож на паттерн декоратор, широко используемый в Mobx. Многие языки, такие как Python, имеют встроенные декораторы и скоро JavaScript также станет их поддерживать. HOC очень похожи на декораторы.
Понять HOC проще на примере кода, чем на словах. Поэтому давайте сначала взглянем на код.
Как мы видим, логика получения данных и обновления состояния похожа на то, как мы это делали в render props. Единственное изменение заключается в том, что класс компонента находится внутри функции. Функция принимает компонент в качестве аргумента, и затем внутри метода render класса мы рендерим переданный компонент, но с дополнительными свойствами (props). Довольно простая реализация паттерна с таким сложным названием, не так ли?
Итак, мы выяснили для чего нам нужны render props, HOC и как мы можем их реализовать.
Остается один вопрос: как выбрать между Render Props и HOC? На эту тему довольно много статей, так что сейчас я не буду об этом говорить.
Заключение
Из мы узнали, для чего нам нужны паттерны Render Props и HOC, суть каждого из них, а также способы их использования для создания переиспользуемых компонентов.