Render Реквизит
Термин «рендер проп» относится к технике совместного использования кода между компонентами React с использованием реквизита, значение которого является функцией.
Компонент с реквизитом рендеринга берет функцию, которая возвращает элемент React, и вызывает его вместо реализации собственной логики рендеринга.
<DataProvider render= {data => (
<h1>Hello {data.target} </h1>
)}/>
Библиотеки, которые используют реквизит рендеринга, включают React Router и Downshift.
В этом документе мы обсудим, почему реквизит рендера полезен, и как написать свой собственный.
Используйте Render Реквизит для сквозных задач
Компоненты являются основной единицей повторного использования кода в React, но не всегда очевидно, как разделить состояние или поведение, которое один компонент инкапсулирует, с другими компонентами, которым необходимо это же состояние.
Например, следующий компонент отслеживает положение мыши в веб-приложении:
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
<h1>Move the mouse around!</h1>
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
Когда курсор перемещается по экрану, компонент отображает свои координаты (x, y) в виде <p>.
Теперь вопрос: как мы можем использовать это поведение в другом компоненте? Другими словами, если другой компонент должен знать о позиции курсора, можем ли мы инкапсулировать это поведение, чтобы мы могли легко поделиться им с этим компонентом?
Поскольку компоненты являются основной единицей повторного использования кода в React, давайте попробуем немного реорганизовать код, чтобы использовать компонент <Mouse>, который инкапсулирует поведение, которое нам необходимо повторно использовать в другом месте.
// The <Mouse> component encapsulates the behavior we need...
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/* ...but how do we render something other than a <p>? */}
<p>The current mouse position is ({this.state.x}, {this.state.y})</p>
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse />
</div>
);
}
}
Теперь компонент <Mouse> инкапсулирует все поведение, связанное с прослушиванием событий mousemove и сохранением позиции (x, y) курсора, но он пока еще не может быть повторно использован.
Например, допустим, у нас есть компонент <Cat>, который отображает изображение кошки, преследующей мышь по экрану. Мы можем использовать опору <Cat mouse = {{x, y}}>, чтобы сообщить компоненту координаты мыши, чтобы он знал, где расположить изображение на экране.
В качестве первого прохода вы можете попробовать визуализировать <Cat> внутри метода render <Mouse>, например:
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class MouseWithCat extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
We could just swap out the <p> for a <Cat> here ... but then
we would need to create a separate <MouseWithSomethingElse>
component every time we need to use it, so <MouseWithCat>
isn't really reusable yet.
*/}
<Cat mouse={this.state} />
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<MouseWithCat />
</div>
);
}
}
Этот подход будет работать для нашего конкретного варианта использования, но мы не достигли цели по-настоящему инкапсулировать поведение с возможностью повторного использования. Теперь каждый раз, когда мы хотим, чтобы положение мыши было для другого варианта использования, мы должны создавать новый компонент (то есть, по существу, другой <MouseWithCat>), который визуализирует что-то специально для этого варианта использования.
Вот здесь и возникает реквизит рендеринга: вместо жесткого кодирования <Cat> внутри компонента <Mouse> и эффективного изменения его рендеринга, мы можем предоставить <Mouse> функцию prop, которую он использует для динамического определения того, что визуализировать -поддержка
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{/*
Instead of providing a static representation of what <Mouse> renders,
use the `render` prop to dynamically determine what to render.
*/}
{this.props.render(this.state)}
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
</div>
);
}
}
Теперь вместо эффективного клонирования компонента <Mouse> и жесткого кодирования чего-то еще в его методе рендеринга для решения для конкретного случая использования мы предоставляем реквизит рендеринга, который <Mouse> может использовать для динамического определения того, что он рендерит.
Более конкретно, рендер-реквизит - это функция-опора, которую компонент использует, чтобы знать, что делать.
Эта техника делает поведение, которое нам необходимо использовать, чрезвычайно переносимым. Чтобы получить такое поведение, визуализируйте <Mouse> с реквизитом рендера, который сообщает ему, что нужно визуализировать с текущим (x, y) курсором.
Что касается рендеринга реквизита, следует отметить одну интересную вещь: вы можете реализовать большинство компонентов высшего порядка (HOC), используя обычный компонент с опорой рендеринга. Например, если вы предпочитаете иметь компонент withMouse HOC вместо компонента <Mouse>, вы можете легко создать его, используя обычный <Mouse> с пропеллером рендеринга:
// If you really want a HOC for some reason, you can easily
// create one using a regular component with a render prop!
function withMouse(Component) {
return class extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Component {...this.props} mouse={mouse} />
)}/>
);
}
}
}
Таким образом, использование рендера позволяет использовать любой шаблон.
Использование реквизита, отличного от рендеринга
Важно помнить, что только потому, что шаблон называется «реквизит рендера», вам не нужно использовать реквизит с именем рендер, чтобы использовать этот шаблон. Фактически, любая опора, являющаяся функцией, которую компонент использует, чтобы знать, что визуализировать, технически является «опорой рендеринга».
Хотя в приведенных выше примерах используется render, мы могли бы с таким же успехом использовать дочернюю опору!
<Mouse children={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>
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!
<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}
</Mouse>
Вы увидите эту технику, используемую в API реакции-движения.
Поскольку этот метод немного необычен, вы, вероятно, захотите явно указать, что childrens должны быть функцией в ваших propTypes при разработке такого API.
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
Предостережения
Будьте осторожны при использовании Render Props с React.PureComponent
Использование реквизита рендеринга может свести на нет преимущество, которое дает использование React.PureComponentif, если вы создаете функцию внутри метода рендеринга. Это связано с тем, что сравнение мелких реквизитов всегда возвращает false для новых реквизитов, и каждый рендер в этом случае будет генерировать новое значение для реквизита рендера.
Например, если продолжить с нашего компонента <Mouse> сверху, если Mouse будет расширять React.PureComponent вместо React.Component, наш пример будет выглядеть следующим образом:
class Mouse extends React.PureComponent {
// Same implementation as above...
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>Move the mouse around!</h1>
{/*
This is bad! The value of the `render` prop will
be different on each render.
*/}
<Mouse render={mouse => (
<Cat mouse={mouse} />
)}/>
</div>
);
}
}
В этом примере каждый раз, когда <MouseTracker> рендерит, он генерирует новую функцию в качестве значения реквизита <Mouserender>, тем самым сводя на нет эффект <Mouse>, расширяющий React.PureComponent в первую очередь!
Чтобы обойти эту проблему, вы можете иногда определить опору как метод экземпляра, например так:
class MouseTracker extends React.Component {
// Defined as an instance method, `this.renderTheCat` always
// refers to *same* function when we use it in render
renderTheCat(mouse) {
return <Cat mouse={mouse} />;
}
render() {
return (
<div>
<h1>Move the mouse around!</h1>
<Mouse render={this.renderTheCat} />
</div>
);
}
}
В случаях, когда вы не можете определить опору статически (например, из-за того, что вам необходимо закрыть опоры и / или состояние компонента), <Mouse> должен вместо этого расширить React.Component.
Новый контент: Composer: менеджер зависимостей для PHP , R программирования