- Оригинальный адрес:Use a Render Prop!
- Оригинальный автор:Michael Jackson
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:yoyoyohamapi
- Корректор:MechanicianW Usey95
возобновить:Я отправил PR в официальные документы React, добавив в него Render props..
Обновление 2: Добавлен раздел, иллюстрирующий, что «дети как функция» — это та же концепция, только с другим именем реквизита.
Несколько месяцев назад я написал в Твиттере:
Аннотация: @reactjs Я могу использовать поддержку рендеринга на обычном компоненте, чтобы делать то, что может делать HOC (компонент более высокого порядка). Отказ от спора.
Я думаю,Шаблон компонента высшего порядкаКак популярное средство повторного использования кода во многих кодах на основе React, он на 100% заменяется обычным компонентом с «поддержкой рендеринга». Термин «не согласен» — это моя дружеская «насмешка» над моими друзьями в сообществе React, после чего последовала серия хороших дискуссий, но, в конце концов, я не смог описать себя в 140 словах, чтобы полностью описать то, что я хотел сказать и расстроенный. яРешил написать более длинную статью в какой-то момент в будущемДавайте обсудим эту тему честно и беспристрастно.
две недели назад, когдаTylerПригласите меня вPhoenix ReactJSГоворя, я думаю, что пришло время обсудить дальше. Я прибыл в Финикс на той неделе.Наши основы React и дополнительные руководства, а также я получаю от своего делового партнераRyanСлышал хорошие новости о конференции, он был ввыступил с речью в апреле.
На съезде моя речь казалась чем-то вроде заголовка вечеринки:Не пишите еще один HOC. ты сможешьОфициальный канал Phoenix ReactJS на YouTubeПосмотрите мое выступление здесь или во встроенном видео ниже:
Если вы не хотите смотреть видео, вы можете прочитать вступление к основному содержанию выступления позже. А если серьезно: видео гораздо веселее 😀.
Если вы пропустите видео и начнете читать и не поймете, о чем я говорю, простоСложите обратно, чтобы посмотреть видеоБар. В выступлении будет больше подробностей.
Проблемы с миксинами
Мой доклад начинается с основной проблемы, которую решают компоненты более высокого порядка:повторное использование кода.
Вернемся к 2015 году, используяReact.createClass
В это время. Предположим, теперь у вас есть простое приложение React, которому необходимо отслеживать и отображать положение мыши на странице в режиме реального времени. Вы можете построить такой пример:
import React from 'react'
import ReactDOM from 'react-dom'
const App = React.createClass({
getInitialState() {
return { x: 0, y: 0 }
},
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
})
},
render() {
const { x, y } = this.state
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
<h1>The mouse position is ({x}, {y})</h1>
</div>
)
}
})
ReactDOM.render(<App/>, document.getElementById('app'))
Теперь предположим, что нам также нужно отслеживать положение мыши в другом компоненте. мы можем повторно использовать<App>
в коде?
существуетcreateClass
В этой парадигме проблема повторного использования кода решается с помощью методов, называемых «примесями». мы создаемMouseMixin
, что позволяет любому отслеживать положение мыши.
import React from 'react'
import ReactDOM from 'react-dom'
// mixin 中含有了你需要在任何应用中追踪鼠标位置的样板代码。
// 我们可以将样板代码放入到一个 mixin 中,这样其他组件就能共享这些代码
const MouseMixin = {
getInitialState() {
return { x: 0, y: 0 }
},
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
})
}
}
const App = React.createClass({
// 使用 mixin!
mixins: [ MouseMixin ],
render() {
const { x, y } = this.state
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
<h1>The mouse position is ({x}, {y})</h1>
</div>
)
}
})
ReactDOM.render(<App/>, document.getElementById('app'))
Проблема решена, верно? Теперь каждый может легкоMouseMixin
на их компоненты и передатьthis.state
свойство мышиx
а такжеy
координировать.
HOC — новый миксин
В прошлом году сES6 class, команда React, наконец, решила использовать вместо них классы ES6.createClass
. Это мудрое решение, никто не поддерживает свою собственную модель классов, когда в JavaScript есть встроенные классы.
Но существует проблема:Классы ES6 не поддерживают миксины. Помимо того, что Дэн не является частью спецификации ES6, он ужеРеагирующий блогДругие проблемы с примесями подробно обсуждаются в сообщении блога, опубликованном на .
Проблема минксинов может быть резюмирована как
- ES6 class. Он не поддерживает миксины.
- не подходит достаточно. Минксины меняют состояние, поэтому может быть трудно понять, откуда взялось какое-либо состояние, особенно когда имеется более одного миксина.
-
конфликт имен. Два миксина, которые обновляют одно и то же состояние, могут перезаписать друг друга.
createClass
API ответит на два миксинаgetInitialState
Проверьте, есть ли у него такой же ключ, и если да, то выдайте предупреждение, но этот метод ненадежен.
Итак, чтобы заменить миксин, многие разработчики в сообществе React, наконец, решили использоватькомпоненты более высокого порядка(называемый HOC) для повторного использования кода. В этой парадигме код проходитдекоратортехнологии, чтобы поделиться. Во-первых, один из ваших компонентов определяет большое количество разметки, которую необходимо отобразить, а затем дополняет ее несколькими компонентами, которые имеют поведение, которым вы хотите поделиться. Итак, вы сейчасУкраситьваш компонент вместосмешатьПоведение, которое вам нужно!
import React from 'react'
import ReactDOM from 'react-dom'
const withMouse = (Component) => {
return class extends React.Component {
state = { x: 0, y: 0 }
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
<Component {...this.props} mouse={this.state}/>
</div>
)
}
}
}
const App = React.createClass({
render() {
// 现在,我们得到了一个鼠标位置的 prop,而不再需要维护自己的 state
const { x, y } = this.props.mouse
return (
<div style={{ height: '100%' }}>
<h1>The mouse position is ({x}, {y})</h1>
</div>
)
}
})
// 主需要用 withMouse 包裹组件,它就能获得 mouse prop
const AppWithMouse = withMouse(App)
ReactDOM.render(<AppWithMouse/>, document.getElementById('app'))
Давайте попрощаемся с миксинами и примем HOC.
В новую эру класса ES6 HOC действительно является решением, которое может элегантно решить проблему повторного использования кода, и сообщество широко приняло его.
На данный момент я хотел бы спросить: что побудило нас перейти на HOC, решили ли мы проблемы, с которыми столкнулись при использовании миксинов?
Давайте посмотрим:
- ES6 class. Это больше не проблема, компоненты, созданные классами ES6, можно комбинировать с HOC.
- недостаточно прямой. Даже с HOC эта проблема все еще существует. В миксине мы не знаем, откуда берется состояние, а в HOC мы не знаем, откуда берутся реквизиты.
- конфликт имен. У нас все еще есть эта проблема. Два HOC с реквизитами с одинаковыми именами столкнутся и перезапишут друг друга, и на этот раз проблема будет более тонкой, потому что React не будет предупреждать об одном и том же имени реквизита.
Другая проблема как с HOC, так и с миксинами заключается в том, что оба используютстатическая композициявместоДинамическая композиция. Спросите себя: где происходит композиция в рамках парадигмы HOC? Когда класс компонента (как в приведенном выше примереAppWithMouse
) создается статическая композиция.
ты не можешьrender
Используйте миксин или HOC в методе, это именно то, что ReactдинамичныйКлюч к комбинированию моделей. когда выrender
Как только композиция будет готова, вы сможете воспользоваться всеми преимуществами жизненного цикла React. Динамическая композиция может быть тривиальной, но, может быть, когда-нибудь ей будет посвящен блог и т. д. Я немного отвлекся. 😅
в общем:HOC, созданные с помощью классов ES6, по-прежнему будут встречаться и использоваться.createClass
Это та же проблема, это считается только одним рефакторингом.
Не говорите сейчас об использовании HOC, мы просто используем новые миксины! 🤗
В дополнение к вышеперечисленным дефектам, поскольку сущность HOC заключается впакеткомпонент и создалсмешатьЗамена примеси для существующего компонента, поэтому,HOC представит много бюрократии. Компонент, возвращенный из HOC, должен вести себя как можно лучше, как компонент, который он обертывает (он должен получать те же реквизиты, что и обертывающий компонент и т. д.). Этот факт делает создание надежного HOC большим количеством шаблонного кода.
как я сказал выше,React RouterсерединаwithRouter
HOCНапример, вы можете увидетьреквизит проходит,wrappedComponentRef,Подъем статического свойства обернутого компонентаИ так в шаблонном коде, когда вам нужно добавить HOC в свой React, вы должны написать их.
Render Props
Теперь есть еще одна техника повторного использования кода, позволяющая избежать проблем примесей и HOC. существуетReact Training, который называется «Реквизиты для рендеринга».
В первый раз я увидел реквизит рендеринга вChengLouна Реакт ЕвропаРазговор о реактивном движении, на конференции он упомянул<Motion children>
API позволяет компоненту совместно использовать интерполированную анимацию со своим родительским компонентом. Если бы мне нужно было определить свойство рендеринга, я бы определил его так:
Свойство рендеринга — это свойство функции типа, которое позволяет компоненту знать, что отображать.
В более общих чертах: вместо того, чтобы делиться поведением компонентов с помощью «примесей» или украшений,Нормальный компонент нужен только функциональный опор, чтобы иметь возможность поделиться каким-то государством.
Продолжая пример выше, мы передадим функцию типаrender
опора для упрощенияwithMouse
HOC к обычному<Mouse>
компоненты. Затем в<Mouse>
изrender
метод, мы можем использовать свойство рендеринга, чтобы компонент знал, как рендерить:
import React from 'react'
import ReactDOM from 'react-dom'
import PropTypes from 'prop-types'
// 与 HOC 不同,我们可以使用具有 render prop 的普通组件来共享代码
class Mouse extends React.Component {
static propTypes = {
render: PropTypes.func.isRequired
}
state = { x: 0, y: 0 }
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
)
}
}
const App = React.createClass({
render() {
return (
<div style={{ height: '100%' }}>
<Mouse render={({ x, y }) => (
// render prop 给了我们所需要的 state 来渲染我们想要的
<h1>The mouse position is ({x}, {y})</h1>
)}/>
</div>
)
}
})
ReactDOM.render(<App/>, document.getElementById('app'))
Концепция, которая должна быть ясной здесь, заключается в том,<Mouse>
Компонент на самом деле вызывает егоrender
способ показать свое состояние<App>
компоненты. следовательно,<App>
Не стесняйтесь использовать это состояние, как хотите, и это прекрасно. 😎
Здесь я хотел бы заявить, что «дети как функция» — этоточно такая же концепция, просто используйтеchildren
опора заменяетrender
опора Я говорюrender prop
не подчеркиваяназванный prop
prop, но подчеркивает концепцию использования реквизита для рендеринга.
Этот метод позволяет обойти все проблемы, с которыми сталкиваются миксины и HOC:
- ES6 class. Нет проблем, мы можем использовать свойство рендеринга в компонентах, созданных классом ES6.
- недостаточно прямой. Нам больше не нужно беспокоиться о том, откуда берутся состояния или реквизиты. Мы можем видеть, какое состояние или свойства доступны через список параметров свойства рендеринга.
- конфликт имен. Теперь не будет автоматического слияния имен свойств, поэтому конфликтов имен никогда не будет.
Кроме того, не будет введена поддержка рендеринга.любая волокитаПотому что ты не будешьпакета такжеУкраситьдругие компоненты. Это просто функция! если вы использовалиTypeScriptилиFlow, вы обнаружите, что теперь проще написать определение типа для вашего компонента, который имеет свойство рендеринга, по сравнению с HOC. Конечно, это другая тема.
Кроме того, комбинированная модель здесьДинамический! Каждая композиция происходит внутри рендера, поэтому мы можем воспользоваться преимуществами жизненного цикла React и естественного потока свойств и состояния.
Используя этот режим, вы можетеЛюбыеHOC заменяет общий компонент реквизитом рендеринга. Мы можем это доказать! 😅
Render Props > HOCs
Более убедительным доказательством того, что свойства рендеринга сильнее, чем HOC, является то, что любой HOC можно заменить на свойства рендеринга, но не наоборот. В следующем коде показано использование универсального кода с реквизитом рендеринга.<Mouse>
компоненты для достиженияwithMouse
HOC:
const withMouse = (Component) => {
return class extends React.Component {
render() {
return <Mouse render={mouse => (
<Component {...this.props} mouse={mouse}/>
)}/>
}
}
}
Внимательные читатели могли заметитьwithRouter
HOC действительно находится в кодовой базе React Router через**реквизит**Достигнуто!
Так еще не взволнован? Идите вперед и используйте свойство рендеринга в своем собственном коде! Попробуйте заменить HOC на компонент с поддержкой рендеринга. Когда вы это сделаете, вы не застрянете в бюрократической волоките HOC, и вы воспользуетесь преимуществами модели динамической композиции, которую дает вам React, что является действительно классной функцией. 😎
MichaelдаReact Trainingчлен и активный член сообщества Reactучастник программного обеспечения с открытым исходным кодом. Если вы хотите узнать о последних тренингах и курсах, просто [подпишитесь на список рассылки] (подпишитесь на список рассылки) иСледите за обучением React в Твиттере.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,React,внешний интерфейс,задняя часть,товар,дизайнЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.