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

Я немного смущен заявлениями: "Оказывает все приложение" и "Передача состояния дочерним компонентам".

Пример 1:

У меня есть приложение todos с AppComponent и TodosListComponent. AppComponent захватывает массив todos из магазина и передает его как свойство TodosListComponent.

Пример 2:

У меня огромное приложение с большим количеством состояний. У меня есть 50 компонентов, которые создают мое приложение. Я хочу передать все состояние из магазинов из AppComponent через все 50 компонентов?

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

Каковы плюсы и минусы каждого? Каково общее соглашение?

Ответы

Ответ 1

Есть несколько способов справиться с этим. Я думаю, что все они действительны и имеют свои компромиссы.

Получить все государство и передать его детям

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

Потенциал этого метода заключается в том, что он упрощает отладку. Если вам нужно изменить способ получения части состояния из хранилища, вам нужно только изменить его в компоненте верхнего уровня, если он передается с тем же именем, остальные компоненты "будут работать". " Если какая-то часть данных неверна, вам нужно только посмотреть в одном месте, чтобы понять, почему.

Недостатком этой техники является то, что я называю "реквизит взрыва" - вы можете в конечном итоге передать много свойств вокруг. Я использую этот метод в приложении потока среднего размера, а фрагмент приложения верхнего уровня выглядит следующим образом:

<section id="col-left">
  <Filters loading={this.state.loading}
            events={this.state.events}
            playbackRate={this.state.videoPlayback.playbackRate}
            autoPlayAudio={this.state.audioPlayback.autoPlay}
            role={this.state.role} />
</section>

<section id="col-center" className={leftPaneActive ? "" : "inactive"}>
  <SessionVideo videoUuid={this.state.session.recording_uuid}
                lowQualityVideo={this.state.session.low_quality_video_exists}
                playbackRate={this.state.videoPlayback.playbackRate} />
  <section id="transcript">
    <Transcript loading={this.state.loading}
                events={this.state.events}
                currentEvents={this.state.currentEvents}
                selection={this.state.selection}
                users={this.state.session.enrolled_users}
                confirmedHcs={this.state.ui.confirmedHcs}

                currentTime={this.state.videoPlayback.position}
                playing={this.state.videoPlayback.playing} />
  </section>
</section>

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

В целом, мне нравится эта отлаживаемая технология, хотя по мере того, как приложение становилось все больше и сложнее, я обнаружил, что это не идея делать это только с одним компонентом верхнего уровня.

Получите все состояние и передайте его как один объект

Один из разработчиков в Facebook упомянул эту технику. Здесь вы получите большую сумку состояния, как и выше, но вы передадите все (или все его части), а не отдельные свойства. Используя React.PropTypes.shape в дочерних компонентах, вы можете гарантировать, что правильные свойства будут переданы.

Поверхность - это преодоление меньшего количества свойств вокруг; приведенный выше пример может выглядеть примерно так:

<section id="col-left">
  <Filters state={this.state} />
</section>

<section id="col-center" className={leftPaneActive ? "" : "inactive"}>
  <SessionVideo session={this.state.session}
                playback={this.state.videoPlayback} />
  <section id="transcript">
    <Transcript state={this.state} />
  </section>
</section>

Недостатком является то, что становится немного сложнее иметь дело с изменениями в форме государства; вместо того, чтобы просто изменять компонент верхнего уровня, вам придется отслеживать всюду, где используется часть данных, и изменять способ доступа этого компонента к свойству. Кроме того, shouldComponentUpdate может потенциально стать немного сложнее для реализации.

Разрешить компонентам получать собственное состояние

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

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

Однако недостатком является то, что ваши компоненты более тесно связаны с магазинами, меняя магазины или предоставляемые ими данные (или интерфейс, через который они предоставляют эти данные) могут заставить вас пересмотреть код для большего количества компонентов.

Кроме того, как упоминалось в комментариях, это может сделать работу сервера немного сложнее. Если вы используете только свойства (особенно на только верхнем уровне), вы можете легко их переносить клиенту и повторно инициализировать React с теми же свойствами. Позволяя магазинам определять свои собственные данные, вам необходимо каким-то образом ввести эти данные в магазины, чтобы компоненты могли получать эти данные.

Обычный подход и тот, который я обычно использую сейчас, состоит в том, чтобы каждый компонент вашего приложения полагался только на реквизиты для состояния глобального приложения, а затем решил, имеет ли смысл (1) подключать их непосредственно к потоку, обернув их в контейнер или (2) разрешить передачу реквизита из некоторого родительского контейнера.


Есть абстракции, которые вы могли бы использовать, чтобы сделать некоторые из этих методов более жизнеспособными. Например, разработчик Facebook сказал об этом в комментарии к Hacker News:

Теперь все ваши данные хранятся в магазинах, но как их получить в конкретный компонент, который в нем нуждается? Мы начали с больших компонентов верхнего уровня, которые извлекали все данные, необходимые для их детей, и передавали их через реквизиты. Это приводит к большому крутому и нерелевантному коду в промежуточных компонентах. То, что мы установили, по большей части, - это компоненты, объявляющие и извлекающие нужные им данные, за исключением некоторых небольших, более общих компонентов. Поскольку большинство наших данных извлекаются асинхронно и кэшируются, мы создали mixins, которые позволяют легко определить, какие данные требуется вашему компоненту, и зацепить выборку и прослушивание обновлений в методах жизненного цикла (componentWillMount и т.д.).

Ответ 2

Джейсон Бонта из Facebook объяснил концепцию "Контейнеры" в своем разговоре React.js Conf 2015.

Подводя итог: контейнеры - это просто компоненты, которые обертывают другие компоненты, и заботятся о любых связанных с данным проблемах, таких как разговоры с магазинами, в то время как базовый компонент ориентирован исключительно на представление (разметка/стили и т.д.) И не заботится откуда поступают данные.

Это делает компонент

  • очень многократно используется, поскольку он может быть обернут другим контейнером, когда данные должны поступать из другого места,

  • не содержат нерелевантного состояния, поэтому проще реализовать и оптимизировать shouldComponentUpdate, и

  • используя композицию, а не mixins для этого, согласуется с тем, что, вероятно, является будущим React с ES6, у которого нет идиоматических миксинов.