Композиция или Наследование

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>
  );
}

Попробовать на CodePen.

Все, что внутри 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 />
      } />
  );
}

Попробовать на CodePen.

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!" />
  );
}

Попробовать на CodePen.

Композиция работает так же хорошо для компонентов определённых как классы:

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}!`);
  }
}

Попробовать на CodePen.

Так что по поводу наследования?

На Facebook, мы используем React c тысячах компонентов, и мы не нашли каких-либо прецедентов, где мы рекомендовали бы создание компонента через иерархию наследования.

Свойства и композиция дадут нам всю необходимую гибкость для настройки внешнего вида и поведения компонента явным и безопасным способом. Помните, что компоненты могут принимать произвольные свойства, в том числе примитивные значения, React-элементы или функции.

Если вы хотите использовать функциональные возможности не связанные с пользовательским интерфейсом между компонентами, мы предлагаем извлечение ихо в отдельный JavaScript-модуль. Компоненты могут импортироваться и использовать эту функцию, объект или класс, не расширяя его.