В 2015 году Дэн Абрамов написал отличную статью, в которой описал шаблон, который он использовал для своей компонентной архитектуры React. Эта статья легла в основу того, что многие разработчики думают о проектировании архитектуры компонентов своего приложения, и продолжает влиять на их решения.
Основной концепцией того, что он отстаивал, были компоненты-контейнеры, или компоненты, подключенные к Redux, и
Презентационные компоненты или компоненты, которые не подключены и имеют минимум зависимостей.

Первоначально он называл их «умными» и «тупыми», что он изменил в своей статье, сказав:

В более ранней версии этой статьи я называл их «умными» и «тупыми» компонентами, но это было слишком резко по отношению к презентационным компонентам и, самое главное, не объясняло разницу в их назначении.

Многие в сообществе использовали термины «умный» и «тупой», и, на мой взгляд, слишком упростили проблему и в итоге создали компоненты, которые были принудительно включены в эту концепцию, вместо того, чтобы быть спроектированными таким образом, который имел наибольший смысл для варианта использования. / проблема, которую пытался решить разработчик.

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

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

Приведенный ниже пример из суть, на который ссылается статья, дает очень простую схему того, как можно разделить компонент, который одновременно загружает данные и представляет их, на два отдельных компонента:

Компонент, подобный этому, будет отклонен при проверке кода из-за того, что * имеет отношение как к представлению, так и к данным:

class CommentList extends React.Component {
  constructor(props) {
    super(props);
    this.state = { comments: [] }
  }
  
  componentDidMount() {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments});
      }.bind(this)
    });
  }
  
  render() {
    return <ul> {this.state.comments.map(renderComment)} </ul>;
  }
  
  renderComment({body, author}) {
    return <li>{body}—{author}</li>;
  }
}

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

// CommentList.js
class CommentList extends React.Component {
  constructor(props) {
    super(props);
  }
  
  render() { 
    return <ul> {this.props.comments.map(renderComment)} </ul>;
  }
  
  renderComment({body, author}) {
    return <li>{body}—{author}</li>;
  }
}
// CommentListContainer.js
class CommentListContainer extends React.Component {
  constructor() {
    super();
    this.state = { comments: [] }
  }
  
  componentDidMount() {
    $.ajax({
      url: "/my-comments.json",
      dataType: 'json',
      success: function(comments) {
        this.setState({comments: comments});
      }.bind(this)
    });
  }
  
  render() {
    return <CommentList comments={this.state.comments} />;
  }
}

Я согласен с намерением этого примера. Эти абстрактные компоненты, такие как список комментариев, не должны знать/заботиться о том, откуда поступают данные.

У меня есть одна проблема: суффикс Container не нужен; Это похоже на вызов компонента CommentListContainerReactComponent. Любой разработчик, работающий над проектом, должен знать об установленных шаблонах, и, глядя на компонент, он будет знать, какому шаблону он следует. Думаю, лучшим названием будет MyCommentsList или MyCommentsCommentList, потому что это лучше объясняет, почему этот компонент уникален, т. е. это CommentList, которому предоставляются данные. из my-comments.json. Еще одна хорошая вещь в следовании этому шаблону именования заключается в том, что он усиливает идею обертывания компонентов, когда вы хотите расширить их функциональность. Представьте, что CommentListContainer используется в нескольких местах, и мы хотим изменить его, чтобы он загружал все комментарии, а не только мои, в той области, в которой он используется. Может быть, я не искал обычаи или искал и не думал, что будет проблемой внести это изменение. Однако, если бы я увидел список MyCommentsList и захотел внести эти изменения, я бы с большей вероятностью создал AllCommentsList и оставил существующий список в покое.

Другая проблема заключается в том, что это не решает того факта, что компоненты могут стать намного более сложными. Разработчикам необходимо сбалансировать скорость с хорошей практикой написания кода. Этот пример показывает, что действительно простой компонент, как показано, должен быть разделен на два. Разработчик всегда должен стремиться найти наиболее разумную абстракцию и соответствующим образом разделить компоненты. Однако бывают случаи, когда компонент может потребоваться разделить на множество подконтейнеров и презентационных компонентов. Именно по этой причине Дэн внес поправку в свою статью, поскольку на практике такое строгое и упрощенное разделение нереально. Также стоит отметить, что иногда это будет невозможно из-за нехватки времени и, как указывает Абрамов:

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

Я думаю, важно помнить, что поправки к его оригинальной статье исходят из реализации и опыта. Оригинальные теории Абрамова не менее верны в своем исходном контексте, но мы всегда должны учитывать, как эти стратегии служат нам в повседневной жизни, поскольку разработчики работают над тем, чтобы поставлять код как можно быстрее и качественнее.