Redux - почему все государство в одном месте, даже состояние, которое не является глобальным?

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

Когда дело доходит до большего приложения, я начинаю задумываться об этом: Почему Redux сохраняет ваше состояние приложения в одном хранилище?

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

Я не уверен в преимуществах наличия состояния для всего в одном месте, когда части этого состояния не имеют ничего общего с другими его частями. Если компонент A не зависит от состояния компонента B и наоборот, не должно ли их состояние храниться в своих компонентах, а не в корне?

Не могу ли я иметь глобально-влияющее состояние в корневом каталоге и состояние, специфичное для каждого компонента в своих собственных компонентах? Я обеспокоен тем, что все компоненты, специфичные для компонента, переходят в цепочку к глобальному объекту состояния (особенно, когда React подчеркивает поток сверху вниз).

Ответы

Ответ 1

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

TL;DR; вы всегда можете легко сохранять и прогнозировать состояние приложения, потому что есть один источник истины.

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

То, что это действительно сводится к контексту. Если у меня есть решение, основанное на знании состояния трех отдельных частей состояния приложения, которые не имеют прямого отношения к их составу пользовательского интерфейса, как мне получить контекст для принятия этого решения и передать результат обратно всему приложению? Без доступа к полному представлению состояния нет простого способа получить эту агрегацию контекста.

Redux (и другие шаблоны, такие как ratom in Reagent) решают это с глобальным унифицированным состоянием. Ваши действия - это просто посланники с изменениями состояния, но ваш Store является владельцем контекста. Без единого магазина ваши компоненты были бы похожи на феодальных военачальников, препирающихся над состоянием их слабо связанных феодальных владений. Магазин является результатом плотной трикотажной олигархии (combineReducers()), которая управляет вашим выражением с помощью железного кулака и оставляет ошибки:)

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

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

С редукторами вы все еще "разделяете" свою логику состояния в коде, но на самом деле все это рассматривается как одно дерево при запуске. Это позволяет вашему государству изменять предсказуемость и атомарность, но позволяет хорошо организовывать, инкапсулировать и разделять проблемы.

Ответ 2

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

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

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

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

Я нашел эту концепцию намного легче изучать и понимать, решая ее из ClojureScript Re-frame и наблюдая за Дэвидом Ноленом видео в Om Next. Re-frame README для проекта также является отличным учебным ресурсом.

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

Простые компоненты

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

Например, нажатие на кнопку редактирования в компоненте NameTag должно открыть редактор, который позволяет пользователю изменять некоторые данные, которые находятся в состоянии компонента Profile (родительский элемент NameTag). Способ решения этой проблемы состоит в том, чтобы передать обратные вызовы обработчиков, которые затем распространяют данные, поддерживающие дерево компонентов. Эта модель приводит к запутыванию потоков под-данных в существующем одностороннем потоке данных приложений React.

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

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

Чистые функции

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

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

Сериализация

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

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

С глобальным состоянием вы можете просто кодировать и декодировать его точно там, где он есть.

Undo/Redo

Чтобы реализовать undo/redo с глобальным состоянием, вам нужно сохранить состояния в списке, а не заменять последнее, когда оно обновляется. Указатель указателя может управлять текущим состоянием, в котором вы находитесь.

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

Воспроизведение действий

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

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