React имеет мощную композиционную модель, и мы рекомендуем использовать композицию вместо наследования для повторного использования кода между компонентами.
В этом разделе мы рассмотрим несколько проблем, с которыми сталкиваются новые разработчики React при работе с наследованием, и покажем, как решить эти проблемы при помощи композиции.
Политика сдерживания
Некоторые компоненты не знают своих дочерних элементах раньше времени. Это особенно характерно для компонентов, таких как Sidebar
или Dialog
, которые представляют общие «коробки» (боксы).
Мы рекомендуем, чтобы такие компоненты использовали передачу специального свойства children
дочерним элементы непосредственно при выводе:
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
Это позволяет другим компонентам передавать произвольные дочерние элементы в JSX:
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}
Все, что внутри JSX тега <FancyBorder>
передается в компонент FancyBorder
, как свойство children
. Так как FancyBorder
отражает/отрисовывает {props.children}
внутри <div>
, переданные элементы отображаются при окончательном выводе.
Менее распространена ситуация, когда вам может понадобиться несколько «дыр» в компоненте. В таких случаях вы можете придумать свой собственный способ обмена вместо children
:
function SplitPane(props) {
return (
<div className="SplitPane">
<div className="SplitPane-left">
{props.left}
</div>
<div className="SplitPane-right">
{props.right}
</div>
</div>
);
}
function App() {
return (
<SplitPane
left={
<Contacts />
}
right={
<Chat />
} />
);
}
React элементы, подобные <Contacts />
и <Chat />
являются только объектами, так что вы можете передать им свойства как и любые другие данные.
Специализация
Иногда мы думаем о компонентах, как об «особые вариантах» других компонентов. Например, мы могли бы сказать, что WelcomeDialog
является частным случаем Dialog
.
В React, это достигается за счет композиции, где более «конкретный» (специфичный) компонент отображается более «общим» и настраивается его свойствами:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
</FancyBorder>
);
}
function WelcomeDialog() {
return (
<Dialog
title="Welcome"
message="Thank you for visiting our spacecraft!" />
);
}
Композиция работает так же хорошо для компонентов определённых как классы:
function Dialog(props) {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
{props.title}
</h1>
<p className="Dialog-message">
{props.message}
</p>
{props.children}
</FancyBorder>
);
}
class SignUpDialog extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.state = {login: ''};
}
render() {
return (
<Dialog title="Mars Exploration Program"
message="How should we refer to you?">
<input value={this.state.login}
onChange={this.handleChange} />
<button onClick={this.handleSignUp}>
Sign Me Up!
</button>
</Dialog>
);
}
handleChange(e) {
this.setState({login: e.target.value});
}
handleSignUp() {
alert(`Welcome aboard, ${this.state.login}!`);
}
}
Так что по поводу наследования?
На Facebook, мы используем React c тысячах компонентов, и мы не нашли каких-либо прецедентов, где мы рекомендовали бы создание компонента через иерархию наследования.
Свойства и композиция дадут нам всю необходимую гибкость для настройки внешнего вида и поведения компонента явным и безопасным способом. Помните, что компоненты могут принимать произвольные свойства, в том числе примитивные значения, React-элементы или функции.
Если вы хотите использовать функциональные возможности не связанные с пользовательским интерфейсом между компонентами, мы предлагаем извлечение ихо в отдельный JavaScript-модуль. Компоненты могут импортироваться и использовать эту функцию, объект или класс, не расширяя его.