Композиция против механизма наследования

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

Включение

Некоторые компоненты заведомо не отличают дочерние функции. Это особенно характерно для таких компонентов, как “Sidebar” или “Dialog”, которые представляют общие «коробки».
В этом случае мы рекомендуем использовать специальные дочерние элементы props «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.

Все, что находится внутри “Fancy Border”, тег JSX передается в компонент “Fancy Border”, как свойство «children».
Так как “Fancy Border” отображает “{props.children}” внутри “”, пройденные элементы отображаются в окончательных выходных данных.
Несмотря на то, что это менее распространено, иногда вам может понадобиться несколько «дыр» в компоненте. В таких случаях вы можете придумать собственную конвенцию вместо «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.

Специализация


Иногда мы думаем о компонентах, как о «специальных случаях» других компонентов. Например, мы могли бы сказать, что “WelcomeDialog” является частным случаем “Dialog”.

В React это также достигается за счет композиции, где более „конкретный“ компонент отображает более „общий“ и конфигурирует его с props:

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 в тысячах компонентов, и мы не нашли каких-либо прецедентов, где мы рекомендовали бы создать наследованный компонент.

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

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