Unetway

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

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

Containment

Некоторые компоненты не знают своих детей раньше времени. Это особенно характерно для таких компонентов, как 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>
  );
}

Все, что находится внутри <FancyBorder> тега JSX, передается в FancyBorder компонент в качестве children. Так как FancyBorder визуализирует {props.children} внутри a <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 />
      } />
  );
}

Реагировать такие элементы как <Contacts /> и <Chat />просто объекты, поэтому вы можете передавать их как реквизит, как любые другие данные. Этот подход может напомнить вам о «слотах» в других библиотеках, но нет никаких ограничений на то, что вы можете передать в качестве реквизита в React.

Специализация (Specialization)

Иногда мы рассматриваем компоненты как «особые случаи» других компонентов. Например, можно сказать, что а 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}!`);
  }
}