此文章是翻译Composition vs Inheritance这篇React(版本v16.2.0)官方文档。

Composition vs Inheritance

React 有一个非常强大的组合模式(composition model),在组件之间,我们建议使用组合(composition)而不是继承(inheritance)。

在这个部分,我们将会考虑几个对React 开发新手来说经常遇到的几个继承问题,展示我们如何使用组合方式来解决它们。

Containment

一些组件提前并不知道它们的孩子元素(children)。对Sidebar 或者Dialog 这些通用的”boxes” 的组件来说尤其常见。

我们通常建议这种组件使用children prop 作为孩子元素(children elments)直接作为输出:

  1. function FancyBorder(props) {
  2. return (
  3. <div className={'FancyBorder FancyBorder-' + props.color}>
  4. {props.children}
  5. </div>
  6. );
  7. }

在JSX 嵌套中可以让其他组件直接传入任意孩子:

  1. function WelcomeDialog() {
  2. return (
  3. <FancyBorder color="blue">
  4. <h1 className="Dialog-title">
  5. Welcome
  6. </h1>
  7. <p className="Dialog-message">
  8. Thank you for visiting our spacecraft!
  9. </p>
  10. </FancyBorder>
  11. )
  12. }

在CodePen 上尝试

<FancyBorder> JSX 标签中的任何东西都可以作为FancyBorder 组件的children prop 传入。由于Fancyborder<div> 标签中渲染{props.chidren} ,所以传入的元素最终被输出显示。

虽然这不是常见的的,但是有时候,在一个组件中,你需要多个”holes”。在这种情况下,你可以根据你自己的习惯来替换使用children

  1. function SplitPanel(props) {
  2. return (
  3. <div className="SplitPanel">
  4. <div className="SplitPanel-left">
  5. {props.left}
  6. </div>
  7. <div className="SplitPanel-right">
  8. {props.right}
  9. </div>
  10. </div>
  11. );
  12. }
  13. function App() {
  14. return (
  15. <SplitPanel
  16. left={
  17. <Contacts />
  18. }
  19. right={
  20. <Chat />
  21. }/>
  22. );
  23. }

在CodePen 上尝试

<Contacts /><Chat /> 这种React 元素就是一个object,你可以像其它数据一样将其作为props 传入。此方法可能会使你记起在其他库中的“插槽(slots)”,但对你在React 作为props 传入的内容没有任何限制。

Specialization

有时我们考虑到组件可以作为其它组件的“特例(special cases)”。例如,我们可以说WelcomeDialogDialog 的一个“特例”。

在React 中,这也可以通过组合来实现,通过使用props 来配置实现在一个 “特殊的(special)” 组件中中渲染一个“常用的(generic)”组件:

  1. function Dialog(props) {
  2. return (
  3. <FancyBorder color="blue">
  4. <h1 className="Dialog-title">
  5. {props.title}
  6. </h1>
  7. <p className="Dialog-message">
  8. {props.message}
  9. </p>
  10. </FancyBorder>
  11. );
  12. }
  13. function WelcomeDialog(){
  14. return (
  15. <Dialog
  16. title="Welcome"
  17. message="Thank you for visting our spacecraft!"/>
  18. );
  19. }

在CodePen 上尝试

通过类定义的组件也可以使用组合方式:

  1. function Dialog(props) {
  2. return (
  3. <FancyBorder color="blue">
  4. <h1 className="Dialog-title">
  5. {props.title}
  6. </h1>
  7. <p className="Dialog-message">
  8. {props.message}
  9. </p>
  10. {props.children}
  11. </FancyBorder>
  12. );
  13. }
  14. class SignUpDialog extends React.Component {
  15. constructor(props) {
  16. super(props);
  17. this.handleChange = this.handleChange.bind(this);
  18. this.handleSignUp = this.handleSignUp.bind(this);
  19. this.state = {login: ''};
  20. }
  21. render() {
  22. return (
  23. <Dialog
  24. title="Mars Exploration Program"
  25. message="How should we refer to you ?">
  26. <input
  27. value={this.state.login}
  28. onChange={this.handleChange} />
  29. <button
  30. onClick={this.handleSignUp}>
  31. Sign Me Up!
  32. </button>
  33. </Dialog>
  34. )
  35. }
  36. handleChange(e) {
  37. this.setState({login: e.target.value});
  38. }
  39. handleSignUp() {
  40. alert(`Welcome aboard, ${this.state.login}!`);
  41. }
  42. }

在CodePen 上尝试

So What About Inheritance?

在Facebook 中,我们使用了React 成千上万个组件,我们还没有发现任何需要继承来实现的组件。

Props 和组合可以让你灵活的配置组件的样式和行为通过准确和安全的方式。记住组件可以接受任何props,包括原生值(primitive values),React 元素或者是函数。

如果你想在组件之间复用非UI(non-UI)功能,我们建议提取一个独立的JavaScript 模块。这个组件无需扩展就可以通过import 来使用这个函数,对象,或者一个类。