{wcademy}

React: компоненты-контейнеры

July 06, 2020

Всем привет! В Реакте есть один паттерн, который оказывает большое влияние на то, как мы разрабатываем приложения — компоненты-контейнеры. Идея довольно простая:

Контейнер получает данные с бэкэнда и затем рендерит соответствующий дочерний компонент. И всё.

Под соответствующим компонентом понимается компонент с таким же именем, к примеру:

StockWidgetContainer => StockWidget
TagCloudContainer => TagCloud
PartyPooperListContainer => PartyPooperList

Уловили идею?

Почему контейнеры?

Предположим, что нам нужно отобразить список комментариев к статье. И если тебе не было известно о концепции компонентов-контейнеров, то всё будет свалено в кучу и код будет выглядеть как-то так:

class CommentList extends React.Component {
  state = {
    comments: [],
  }

  componentDidMount() {
    fetch('https://jsonplaceholder.typicode.com/comments')
      .then(response => response.json())
      .then(comments => this.setState({ comments: comments }))
  }

  render() {
    return (
      <ul>
        {this.state.comments.map(c => (
          <li key={c.id}>
            {c.body}--{c.name}
          </li>
        ))}
      </ul>
    )
  }
}

(ссылка на codesandbox)

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

  • Переиспользование

Компонент не может быть переиспользован, всё жёстко прибито гвоздями. Разве что в случае если где-то потребуется отобразить один в один такие же комментарии, но вряд ли.

  • Структура данных

Компоненты должны явно показывать, какие данные они ожидают. Для этого идеально подходят пропсы совместно с PropTypes. Компонент тихо перестанет отображать комментарии, если апи на бэкэ хоть немного изменится (скорее всего, бэки специально ломать апи не будут, но всякое возможно, особенно при использовании каких-то third-party api).

Давайте ещё раз. В этот раз с контейнерами.

Первым делом, отделим логику получения данных в отдельный компонент-контейнер.

class CommentListContainer extends React.Component {
  state = {
    comments: [],
  }

  componentDidMount() {
    fetch('https://jsonplaceholder.typicode.com/comments')
      .then(response => response.json())
      .then(comments => this.setState({ comments: comments }))
  }

  render() {
    return <CommentList comments={this.state.comments} />
  }
}

Теперь переработаем компонент CommentList, передавая комментарии, как пропсы:

class CommentList extends React.Component {
  render() {
    return (
      <ul>
        {this.props.comments.map(c => (
          <li key={c.id}>
            {c.body}--{c.name}
          </li>
        ))}
      </ul>
    )
  }
}

(ссылка на codesandbox)

Так что мы получили?

Многое =)

  • Мы разделили бизнес-логику и логику отображения.
  • Мы сделали наш CommentList переиспользуемым.
  • Мы дали возможность компоненту проверять типы входных параметров и он будет громко ругаться, если в него начнут подсовывать что-то неправильное.

Удачного кодинга!

🚀  Если узнал из статьи что-то полезное, ставь лайк и подписывайся на наш канал в Телеграм или группу ВК. Обсудить статью можно в нашем уютном чатике 😏

© 2019 - 2022, {wcademy}