Compositon vs Inheritance

React có một composition model rất mạnh mẽ, và chúng tôi gợi ý sử dụng composition thay vì dùng kế thừa để sử dụng lại code giữa các component.

Trong phần này, chúng ta sẽ cân nhắc vài vấn đề nơi các dev bắt đầu với React thường dùng với kế thừa, và chỉ ra cách chúng ta có thể giải quyết chúng với composition.

Containment
Vài component không biết trước về con của chúng. Điều này rất bình thường với những component như Sidebar hay Dialog đại diện cho các boxes chung chung.

Chúng tôi gợi ý rằng những component như thế sử dụng các prop đặc biệt children để chuyền các element con một cách trực tiếp vào đầu ra:

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}    </div>
  );
}

Điều này giúp các component truyền chilren bất kỳ cho chúng bằng cách gán mã 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>
  );
}

Thử với CodePen

Bất cứ thứ gì bên trong thẻ JSX <FancyBorder> được truyền vào component FancyBorder như prop children. Vì FancyBorder hiển thị {props.children} bên trong <div>, các element đã được truyền xuất hiện ở trong đầu ra cuối cùng.

Mặc dù điều này ít phổ biến hơn, đôi khi bạn có thể cần nhiều “holes” trong một component. Trong trường hợp như thế bạn có thể đưa ra convention của bạn thay vì sử dụng 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 />
      } />
  );
}

Thử với CodePen

Các element của React như <Contacts /><Chat /> chỉ là các object, nên bạn có thể truyền chúng như các prop giống như bất kỳ dữ liệu khác. Cách tiếp cận này nhắc nhở bạn về “slots” trong các thư viện khác nhưng không có giới hạn về thứ gì bạn có thể truyền như các props trong React.

Specialization

Đôi khi chúng ta nghĩ về các component đang là “special cases” của các component khác. Ví dụ, chúng ta có thể nói rằng WelcomeDialog là trường hợp đặc biệt của Dialog.

Với React, điều này cũng đạt được bởi composition, nơi mà component “specific” hơn hiển thị component “generic” hơn và
cấu hình với prop:

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

Thử với CodePen

Composition hoạt động cũng tốt như các component được định nghĩa như các class:

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

Thử với CodePen

So What About Inheritance?
Ở Facebook, chúng tôi sử dụng React trong hàng nghìn component, và chúng tôi chưa từng tìm thấy trường hợp mà chúng tôi có thể khuyên nên tạo component kế thừa phân cấp.

Prop và composition cho bạn tất cả linh hoạt mà bạn cần cho việc tùy chỉnh giao diện và hành vi theo cách rõ ràng và an toàn. Nhớ rằng các component có thể nhận prop bất kỳ, kể cả giá trị nguyên thủy, các element hay các hàm.

Nếu bạn muốn tái sử dụng chức năng không có giao diện giữa các component, chúng tôi khuyên bạn nên tách chúng thành các module JavaScript rời. Các component có thể “import” chúng và sử dụng như hàm, object hoặc là class mà không cần “extend”.