Skip to content

Latest commit

 

History

History
186 lines (150 loc) · 7.18 KB

composition-vs-inheritance.md

File metadata and controls

186 lines (150 loc) · 7.18 KB
id title permalink redirect_from prev next
composition-vs-inheritance
Composition vs Inheritance
docs/composition-vs-inheritance.html
docs/multiple-components.html
lifting-state-up.html
thinking-in-react.html

Ghi chú:

  • Link bài gốc: Xem tại đây
  • Với người tiếp xúc đầu tiên với JavaScript nói riêng và front-end development nói chung, "composition" và "inheritance" là hai khái niệm không hiểu được. "Inheritance" có thể được dịch là "kế thừa". Những phần này, có thể được hiểu dần thông qua thực hành, kết hợp với đọc những bài sau:
  • Với phần này, tạm bỏ "Inheritance" ra khỏi đầu, chỉ tập trung cảm nhận "Composition" hoạt động như thế nào, làm việc/ tạo ra nó ra sao.

React sử dụng model với kỹ thuật "composition" mạnh mẽ, và chúng tôi khuyến cáo dùng kỹ thuật này hơn là dùng "inheritance" nhằm tái sử dụng code giữa các component.

Trong phần này, ta sẽ cùng xem một vài vấn đề mà lập trình viên khi mới học React thường nghĩ ngay đến "inheritance", để rồi chỉ ra rằng ta hoàn toàn có thể giải quyết với kỹ thuật "composition".

Cô lập hóa

Có những component không hề biết trước các component con của nó sẽ là gì, ví dụ như component Sidebar hoặc Dialog vốn đơn giản chỉ là những chiếc "hộp".

Facebook khuyến cáo những component kiểu này nên sử dụng prop đặc biệt children để truyền trực tiếp những phần tử con vào đầu ra:

We recommend that such components use the special children prop to pass children elements directly into their output:

Ví dụ với component FancyBorderWelcomeDialog dưới đây:

function FancyBorder(props) {
  return (
    <div className={'FancyBorder FancyBorder-' + props.color}>
      {props.children}
    </div>
  );
}
function WelcomeDialog() {
  return (
    <FancyBorder color="blue">
      <h1 className="Dialog-title">
        Welcome
      </h1>
      <p className="Dialog-message">
        Thank you for visiting our spacecraft!
      </p>
    </FancyBorder>
  );
}

Try it on CodePen.

Bên trong WelcomeDialog, Bất kỳ thứ gì kẹp giữa JSX tag <FancyBorder></FancyBorder> sẽ được truyền vào cho component FancyBorder. Component này sẽ nhận thông tin truyền vào như là prop children của mình. Bởi FancyBorder render {props.children} bên trong một <div>, thông tin kia sẽ xuất hiện trong kết quả HTML trả về.

Ngoài ra, trường hợp bên dưới đây có thể ít phổ biến hơn, nhưng sẽ có lúc bạn cần một vài "lỗ trống" trong component của mình. Trong những trường hợp này, bạn sẽ cần sử dụng cách viết truyền thóng 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 />
      } />
  );
}

Try it on CodePen.

Những React element như <Contacts /><Chat /> chỉ đơn thuần là object, vì vậy ta có thể truyền chúng dưới dạng props như bất kỳ data nào khác. Cách viết này có lẽ làm bạn liên tưởng đến khái niệm "slots" ở trong các thư viện khác. Nhưng ở trong React, sẽ không có giới hạn nào với việc bạn truyền gì vào props.

Chuyên môn hóa

Cũng có lúc ta coi component này là trường hợp đặc biệt của component kia. Ví dụ, ta có thể nói WelcomeDialog là trường hợp đặc biệt của Dialog.

Trong React, việc này đạt được thông qua một kỹ thuật tên là "composition". Kỹ thuật này giúp một component "đặc biệt" (chính là WelcomeDialog) render một component "tổng quát" (chính là Dialog), cấu hình component tổng quát này với 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!" />
  );
}

Try it on CodePen.

Kỹ thuật "composition" kết hợp hoàn hảo với các component dạng 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}!`);
  }
}

Try it on CodePen.

Vậy còn kỹ thuật "Inheritance" thì sao?

Tại Facebook, chúng tôi sử dụng React trong hàng ngàn components, nhưng chưa tìm thấy trường hợp nào cần tạo component thông qua kỹ thuật "inheritance hierarchies".

Props và kỹ thuật "composition" đã cho ta mọi sự linh hoạt cần thiết để tùy biến một component, cả vẻ bề ngoài lẫn khả năng tương tác, vừa tường minh, vừa an toàn. Hãy nhớ rằng component có thể nhập props bất kỳ:

  • một giá trị dạng primitive JS (như number, string, object, v.v.)
  • một React element
  • hay là một function.

Nếu bạn muốn tải sử dụng những tính năng không liên quan đến giao diện giữa các component, chúng tôi gợi ý nên tách riêng những tính năng đó sang các module JavaScript độc lập. Khi muốn dùng tính năng ấy, ta chỉ cần import các module kia, rồi dùng các function, object, hay class mà không phải viết lại.