最佳实践:封装组件或使用 props.children 进行深度组合

Best Practice: encapsulate components or compose deeply using props.children

我面临着如何组合我的组件的决定,我想知道社区是否对最佳实践有任何想法。

1:封装组件隐藏细节

所有者渲染:

<List ... />

渲染列表:

<Item ... />

项目渲染:

<div>...</div>

在这个版本中,组件的可重用性不是很好,因为它们知道很多关于域的详细信息。所有者对列表中的项目一无所知。

2:嵌套组件靠近顶部,有状态视图控制器

所有者渲染:

<List>
  <Item>
    <div>...</div>
  </Item>
</List>

在此版本中,Owner 知道其子项的所有组成,但组件本身并不是很有用,因为它们对域一无所知。然而,它们更可重复使用。

3: 将通用组件封装在领域特定组件中

这是选项 1 和选项 2 的混合体

所有者渲染:

<DomainList ... />

DomainList 呈现:

<List ...>
  <DomainItem .../>
</List>

DomainItem 呈现:

<Item ...>
   ...
</Item>

所有者对通用列表、域项目或通用项目一无所知。

哪种方法更好?有没有人在这些方法中取得成功或失败?发生什么事了?

组件的可重用性并不低,因为它们决定了关于它们应该如何呈现的大部分事情。您甚至可以争辩说它们更易于重用,因为使用它的组件不需要知道它是如何呈现的。

当你开始思考这些事情时,很容易陷入分析瘫痪状态。我创建 React 组件的方法是:

  1. 尽量避免在组件中有状态,并将状态作为 props 传递,而不是从层次结构的更高层传递。
  2. 仅对不知道其中包含什么内容的包装器组件使用 this.props.children。弹出窗口、选项卡视图等常规内容
  3. 不要开始将内容提取到其他组件中,因为 "I might reuse this in some other place later"。相反,我只在我知道我在其他地方需要它时才提取。
  4. 我没有像组件的作者那样思考,而是尝试像组件的用户那样思考。问自己 "How would I want to pass data to this component?"、"Does the user of this component need to be alerted when something changes in it?"、"How much flexibility do I need when using this component?" 等问题。虽然泛化很容易走到这里,所以我也尽量不要抽象太多。

我倾向于看到两种不同类型的组件。通用的东西要么非常通用以至于我可以在项目之间重用它们,要么是特定于应用程序但足够通用以至于我会在应用程序的不同部分出于不同目的大量使用它们。 然后是具有非常特定用途的特定组件,例如 "render this todo list" 或 "render this form to edit the todo"。这些倾向于使用通用组件,但绝对不是相反。

当我开始创建组件时,我通常很清楚它将成为这些类型中的哪一种。对于通用组件,我倾向于使它们尽可能灵活和开放。对于特定用途的,我让它们更像黑盒子,有点像 "Just give me the data and I'll take it from there"。因为它们具有非常具有描述性的名称,并且知道所有关于如何呈现自身的知识,所以在阅读使用它们的代码时很容易理解发生了什么。

在您的示例中,我会说 List 组件是一个通用组件,我肯定希望它变得灵活。我可能也有一个特定的组件用于我正在构建的列表,类似于 TodoList 可能看起来像:

var TodoList = React.createClass({
  render() {
    return (
      <List>
        {this.props.todos.map(todo => <ListItem>{todo.text}</ListItem>)}
      </List>
    );
  }
});

另一种选择是这样的:

var ListItem = React.createClass({
  render() {
    return <li>{this.props.item.text}</li>;
  }
});

var TodoList = React.createClass({
  render() {
    return <List items={this.state.items} listItemComponent={ListItem} />;
  }
});

在这种情况下,List 组件遍历所有项目并使用传递的 listItemComponent 组件来呈现项目。

TL;DR

首先制作知识渊博的非常具体的组件,然后在看到重用用例时将它们重构为更通用的组件。