Redux: Вопрос: Как выбрать между хранилищем Redux и состоянием React?

Созданный на 27 янв. 2016  ·  26Комментарии  ·  Источник: reduxjs/redux

question

Самый полезный комментарий

Используйте React для эфемерного состояния, которое не имеет значения для приложения в глобальном масштабе и не изменяется сложным образом. Например, переключатель в каком-либо элементе пользовательского интерфейса, состояние ввода формы. Используйте Redux для состояния, которое имеет глобальное значение или изменяется сложным образом. Например, кешированные пользователи или черновик публикации.

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

Практическое правило: делайте то, что менее неудобно.

Все 26 Комментарий

Используйте React для эфемерного состояния, которое не имеет значения для приложения в глобальном масштабе и не изменяется сложным образом. Например, переключатель в каком-либо элементе пользовательского интерфейса, состояние ввода формы. Используйте Redux для состояния, которое имеет глобальное значение или изменяется сложным образом. Например, кешированные пользователи или черновик публикации.

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

Практическое правило: делайте то, что менее неудобно.

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

Чтобы это состояние прослушивалось двумя или более контейнерами, оно также должно быть в магазинах?

Я согласен с @gaeron в различии эфемерных и постоянных. Но на самом деле я думаю об этом в трех категориях . Одно из них - это состояние пользовательского интерфейса, о котором он говорит, и я также считаю его эфемерным. Другой - это состояние приложения, которое на самом деле является постоянным ядром приложения. Но третье, что находится между ними, - это состояние маршрутизации . Я использую термин «маршрутизация», потому что он знаком людям, но я абстрагирую его до состояния «выбор представления», поскольку я считаю, что он лучше охватывает настольные, веб- и мобильные устройства.

Теперь вы можете утверждать, что это состояние пользовательского интерфейса, потому что оно определяет, что люди видят (во многом как, например, состояние вкладок). Но я вижу два различия. Во-первых, состояние сериализуется (то есть в виде URL-адреса) и отправляется другим людям. Таким образом, все должно происходить в состоянии маршрута, если вы хотите, чтобы люди могли выполнять «глубокую ссылку» прямо на это конкретное состояние пользовательского интерфейса . Во-вторых, во многих случаях начальное состояние маршрута или изменение маршрута вызывает изменение состояния приложения (т. Е. Загрузку данных для просмотра). Конечно, действия в пользовательском интерфейсе делают то же самое. Но различие, которое я делаю, заключается в том, что вы можете (и должны) иметь состояние маршрута без какого-либо представления или рендеринга именно для проверки «логики приложения» при изменении состояния маршрута. И это потому, что представление и рендеринг не нужно задействовать, что делает его частично состоянием приложения, ИМХО.

Как это связано с вопросом о Redux vs. React для управления состоянием? Состояние приложения - это область Redux, а состояние пользовательского интерфейса - это область React. Но состояние маршрутизации должно (на мой взгляд) управляться Redux, даже если его можно рассматривать как состояние пользовательского интерфейса (см. Встроенные ссылки, чтобы подробнее обсудить, почему я так думаю).

Чтобы быть ясным, комментарий @gaearon о том, что движется, все еще применяется. Но я считаю полезным различать эти разные случаи.

Состояние приложения - это область Redux, а состояние пользовательского интерфейса - это область React.

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

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

Во-первых, я, конечно, не собирался вкладывать слова в

Во-вторых, я полностью согласен с @gaearon. Я также твердо верю в подход с использованием единого дерева состояний. Когда я говорил о состоянии пользовательского интерфейса, я имел в виду действительно незначительные вещи (например, какая вкладка выбрана в данный момент), которые на самом деле могут не иметь отношения к состоянию приложения (как раз обсуждается @gaearon ).

Итак, позвольте мне прояснить свою позицию. Я согласен с тем, что почти все должно быть в Redux , включая состояние маршрута (как я сделал в своей реализации TodoMVC с Redux и TypeScript . Я специально упомянул состояние маршрута и выделил его, потому что я думаю (и вы можете увидеть это в этом TodoMVC реализация), что он определенно принадлежит Redux (и вообще не должен быть связан с рендерингом) и определенно не является «состоянием пользовательского интерфейса».

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

Я думаю, что этот вопрос действительно субъективен и сложен, поэтому сегодня я принял трудное решение со своей командой, не беспокойтесь:

  • для одноразового контейнера, который имеет соединение с Redux, просто поместите все в хранилище Redux, даже крошечное состояние пользовательского интерфейса, например, если модальное окно открыто, больше не используйте this.setState .
  • для многоразового компонента, не имеющего ничего общего с Redux, используйте состояние React.

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

@gaearon @ lionng429 @xogeny Есть ли недостатки, которые вы можете вспомнить у этого подхода?

@inetfuture Я, как правило, немного скупо combineReducers и connect для подключения (и, по крайней мере, сохранения ограничений типа). Так что это, вероятно, не репрезентативная точка зрения. Но я скажу, что почему вы говорите «просто положите все в магазин Redux», я бы побеспокоился, что вы в конечном итоге получите кухонную раковину государства. Я думаю, что тот факт, что я использую TypeScript, означает, что для расширения состояния приложения требуются некоторые усилия, не обязательно плохо, потому что это заставляет меня решить: «Действительно ли мне это нужно?» вместо того, чтобы позволить вещам просто накапливаться.

В качестве альтернативной мысли: состояние локального компонента может быть полезно для управляемых входов, которым требуется быстрое время обновления, а также для уменьшения количества фактических запускаемых действий. В моем текущем прототипе я собрал HOC редактирования формы, который получает текущий редактируемый элемент как опору. Он также обрабатывает события изменения ввода формы, сохраняя измененное поле в его состоянии, а затем вызывая создателя противодействующего действия «EDIT_ITEM_ATTRIBUTE» с объединенными локальными изменениями WIP. В результате поля формы обновляются немедленно, потому что повторно отображается только сама форма, и запускается гораздо меньше действий (например, если бы я удерживал A в течение нескольких секунд, только значение «AAAAAAAAAAAA» было бы отправляться как действие, а не как «А», «АА» и т. д.).

У меня есть HOC как суть на https://gist.github.com/markerikson/554cab15d83fd994dfab , если кому-то интересно.

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

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

В наших приложениях мы решили эту проблему в стиле подключения. https://github.com/Babo-Ltd/redux-state

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

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

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

@gaearon, не

@idavollen Могу дать парочку:

  • раскрывающееся состояние открытия / закрытия, иначе вам придется отслеживать все компоненты раскрывающегося списка в вашем магазине
  • когда вы отклоняете <input> change, вы можете использовать состояние для мгновенного обновления значения (и вы не рискуете ненужным повторным рендерингом других компонентов) и обновлять хранилище в отлаженной манере

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

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

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

Мне интересно, как лучше всего управлять этими состояниями флажков в этом случае?

@idavollen , эти флажки неконтролируемы ? Если нет, вы все равно их повторно визуализируете, поэтому вы можете отслеживать, какие из них выбраны в состоянии компонента, и использовать это для выбора, какие части данных будут отправлены.

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

@tiberiumaxim По моему опыту (я разрабатываю приложения на react и redux, которые работают на маломощных устройствах, таких как Smart TV). В большинстве случаев обновление состояния компонента будет более производительным только потому, что различие, которое должно произойти, относится к меньшему набору данных, и диспетчеризации не требуется. В случаях, когда доступ к этому состоянию требуется только компоненту или дочерним элементам, было бы лучше придерживаться локального состояния компонента, и это часто имеет место с состоянием, связанным с пользовательским интерфейсом, если, однако, вам нужно сохранить эти данные в хранилище redux в какой-то момент - вам может потребоваться комбинация локального (компонентного) и глобального (redux) состояния и убедиться, что вы сохраняете эти данные в хранилище redux в нужное время, то есть когда пользователь уходит со страницы, или что-то в этом роде. Может быть, если бы вы могли пролить больше света на свой конкретный случай - я мог бы дать вам более подробную информацию

Вам решать, какое состояние будет в Redux, а что останется локальным для компонента. Вы можете прочитать http://redux.js.org/docs/faq/OrganizingState.html#organizing -state-only-redux-state и http://redux.js.org/docs/faq/Performance. html # performance -scaling для получения дополнительной информации.

@deowk, спасибо, что поделились своими знаниями о производительности, я думал то же самое, но мне нужно было подтверждение.
Также @markerikson благодарит за ссылки, я еще раз их посмотрю.

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

Эту архитектуру можно реализовать вручную или использовать какую-нибудь библиотеку вроде redux-фрактал, redux-ui и так далее.

Или, как указал Дэн, вы можете даже реализовать подход в стиле редуктора для обновления состояния компонента! См. Https://twitter.com/dan_abramov/status/736310245945933824

Я хочу знать, где хранить состояние пользовательского интерфейса, например индикатор активности и модальное открытие или закрытие. Вызывает ли использование setState проблему при модульном тестировании реагирующих компонентов?

@ akshay2604 : опять же, setState при тестировании компонентов, особенно если вы используете библиотеку Enzyme для их тестирования.

Разрешите поделиться своими мыслями.

Я делю состояние на 3 широкие категории в зависимости от того, как оно связано с вводом / выводом приложения:

  • «кеш»: состояние, которое является зеркалом постоянных данных. Пример:
{ "todos": [{ id: 1, title: "title" }, { id: 2, title: "title" }] }
  • «изменения»: состояние, описывающее ожидающие изменения постоянных данных. Пример:
{ "changeTodo": { title: "title", action: "add", done: false },
{ id: 1, title: "changed title", action: "modify", done: true }, { id: 2, action: "delete" } }
  • "просмотр": состояние, которое передает параметры просмотра, такие как текущая фильтрация, сортировка и т. д. Пример:
{ "displayOptions": { searchTerm: "title", sort: ["title", "desc"], filter: "done=false" } }

Вы не смешиваете cache с changes , вы не смешиваете cache с view , вы не смешиваете view с changes .

Конечно, вам нужно как-то их объединить, чтобы отобразить в одном компоненте реакции. Таким образом, ваш "объединенный" объект становится:

{ "todos": [{id: undefined, title: "title", done: false }, { id: 1, title: "changed title" }] }

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

Сопровождающие Redux скажут вам: «Эй, вы должны реализовать логику вашего приложения в виде нескольких редукторов и нескольких событий, которые будут создавать разные части вашего« объединенного »объекта».
Я считаю, что это неправильный подход. Это означает, что вы не сможете уйти от редукции. То, что задумывалось как инструмент для управления вашим состоянием, по сути становится реализацией логики вашего

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

Но что, если для одного действия требуются оба одновременно? Вот очень распространенный вариант использования:

  1. [context] Хранилище Redux - это одностворчатый { modelItems: myModelItems } . Кэширует (часть / проекцию) модели сервера
  2. [context] Представление состоит из единственного интеллектуального компонента MyItemsView который отображает UI-список сохраненных / кэшированных элементов. При установке компонент запускает выборку элементов модели. Компонентный ввод: myModelItems . Компонентный вывод: fetchModelItems action-trigger. Внутреннее устройство компонентов: state.busy и state.errorId
  3. [план] Когда MyItemsView запускает fetchModelItems , на сервер отправляется HTTP-запрос, чтобы получить модель и кэшировать ее в магазине. Это асинхронное действие имеет 4 фазы и столько же перехватчиков: onStarted (HTTP-запрос вот-вот стартует, загрузка счетчика начинается в компоненте), onEnded (HTTP-ответ отправлен, загрузка счетчика останавливается в компоненте. , статус ответа по-прежнему не просматривается), onFailed (в компоненте отображается уведомление об ошибке) и onSucceed (хранилище Redux обновляется, затем кеш-модель, которую он содержит, перенаправляется в компонент для рендеринга)

Следовательно, как разбить триггер действия fetchModelItems так, чтобы:

  • onStarted , onEnded и onFailed не попадают в магазин Redux? Это потому, что onStarted и onEnded являются временными состояниями пользовательского интерфейса, которые не должны взаимодействовать с кешем модели. То же самое касается onFailed (кеш модели остается неизменным, а пользовательский интерфейс отображает уведомление об ошибке). MyItemsView будет отображать здесь UI-состояние, а не кешированную модель
  • onSucceed попадает в магазин Redux? Это потому, что он выиграл лотерею кеширования моделей, получил доступ к источнику истины, промыл и регидратировал, а затем отобразил свежий myModelItems в MyItemsView

Что касается реализации в моей текущей попытке, Redux Thunk заботится о триггере действия fetchModelItems . Мне кажется, что я выбрал там путь Redux и что я больше не могу передумать, чтобы сказать: «Просто передайте успех async в хранилище Redux, а все остальное оставьте в состоянии компонента React».

Озадаченный этим, я беру успехи в память и пересылаю их подключенному компоненту (то есть контейнеру Redux). Но нет загрузочного счетчика и жалкого банкомата с сообщениями об ошибках, регистрируемыми в консоли. Я не хочу определять store.ui.myItemsView.busy или store.ui.myItemsView.errorId , а просто получаю эту информацию в состоянии компонента.

@pascalav :
Это баг-трекер, а не система поддержки. По вопросам использования используйте Stack Overflow или Reactiflux, где гораздо больше людей готовы помочь вам - вы, вероятно, быстрее получите лучший ответ. Благодаря!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги