Для чего нужен selector ngrx
Angular: пример использования NGRX
Эта статья является продолжением поста «Angular: понятное введение в NGRX»
Пример NGRX
В нашем примере будет список пользователей, страница сведений о пользователе и некоторая начальная информация о конфигурации, которую вы должны получить при запуске приложения. Мы собираемся реализовать некоторые важные потоки NGRX.
Установка библиотеки
Мы собираемся использовать Angle Cli для создания проекта, а затем мы добавим библиотеки ngrx.
Добавим необходимые библиотеки NGRX:
Мы устанавливаем почти все библиотеки экосистемы ngrx. Большинство из них достаточно ясно описывают их назначение: ядро, хранилище, эффекты, но есть пара, для которой вы можете задаться вопросом, для чего они нужны?
Структура папок для хранилища
Начнем с обсуждения файловой структуры хранилища. Эта файловая структура и вся конфигурация хранилища должна существовать в основном модуле вашего приложения, но в нашем примере она отсутствует, поэтому хранилище будет существовать в основном модуле нашего приложения (шаги практически одинаковые, если вы делаете это в основном модуле).
Структура папок является представлением фактической композиции хранилища. У вас будет основная папка с названием «store» и пять подпапок, которые представляют каждого из ключевых игроков магазина: «Actions», «Effects», «Redurs», «Selectors» и «State».
Создание хранилища и начальных значений
Как мы уже упоминали ранее, у нас будет два основных раздела: наше приложение, пользователи и конфигурация. Для них обоих нам нужно создать состояние и начальное состояние, а также сделать то же самое для состояния приложения.
Мы создали два интерфейса для определения пользователя и конфигурации. У нас также есть один для ответа HTTP пользователя, это просто массив IUser.
Начнем с состояния пользователя (store/state/user.state.ts):
Что мы здесь делаем:
Наконец, нам нужно сгенерировать состояние приложения (store/states/app.state.ts):
Создание Действий
Обязательно прочитайте определение действия, которое мы обсуждали в предыдущей статье.
Нам нужно создать действия для пользователей и настройки. Начнем с действий пользователя (store / actions / user.actions.ts):
Давайте немного пройдемся по коду:
Ничего нового здесь, вы, вероятно, теперь чувствуете себя комфортно со всем, что происходит в этом файле.
Отлично, мы уже определили состояние и действия… давайте создадим редукторы!
Создание Редукторов
Обязательно прочтите определение редукторов, которое мы обсуждали в предыдущей статье.
У нас будут редукторы, реагирующие на некоторые действия, потому что другие будут обрабатываться эффектами, которые мы собираемся реализовать позже.
Нам понадобится редуктор для пользователей и другой для конфигурации, но нам также понадобится генерировать редукторы приложений, давайте начнем с рассмотрения редукторов пользователей (store/redurs/user.reducers.ts):
Давайте обсудим реализацию:
Наконец, давайте посмотрим на редукторы приложений (store):
Здесь мы добавляем все редукторы на карту редукторов действий приложения. Мы используем карту редуктора действий для дополнительной проверки типов. Позже мы собираемся предоставить это приложение редукторы для модуля store.
Добавим Эффекты
Обязательно прочтите определение «Эффекты», которое мы обсуждали в предыдущей статье.
Вы, наверное, уже заметили, что в редукторах мы обрабатываем не все действия. Это потому, что мы собираемся обработать пропущенные действия в эффектах, потому что эти действия имеют побочные эффекты.
Начнем с пользовательских эффектов (store/effects/user.effects.ts):
Много что происходит в этом файле. Давайте попробуем объяснить их:
Рассмотрим эффекты конфигурации (store/effects/config.effects.ts):
Теперь пришло время поговорить о селекторах…
Создание Селекторов
Обязательно прочитайте определение Селекторов, которое мы обсуждали в предыдущей статье.
Нет смысла повторять выборки срезов нашего состояния повсеместно, поэтому давайте создадим некоторые селекторы, которые мы можем использовать повторно.
Как всегда, давайте сначала посмотрим на пользовательские селекторы (store/selectors/user.selectors.ts):
Это действительно понятно, потому что мы не делаем никаких трансформаций данных в наших селекторах, а просто возвращаем срез хранилища, на который ссылается селектор, используя функцию createSelector из ngrx/store.
Первый параметр — это фрагмент хранилища, которое будет использоваться для получения данных (это может быть массив с несколькими частями состояния), второй параметр — анонимная функция, которая будет решать, что будет возвращать селектор.
Давайте посмотрим на конфигурационные селекторы (store/selectors/config.selectors.ts):
Итоговая настройка
Отлично, мы создали все, что нужно нашему хранилищу, но нам пока не хватает одной вещи — собрать все воедино. Я собираюсь сделать это в модуле приложения, но вы можете применить то же самое в основном модуле вашего приложения.
Давайте дополним модуль приложения:
Что мы здесь сделали:
Теперь мы наконец сделали… мы можем использовать наше хранилище в компонентах!
Использование хранилища в некоторых компонентах
Мы приближаемся к концу! Давайте посмотрим, как использовать наше хранилище…
Во-первых, давайте получим конфигурацию при запуске приложения:
Как это связывать с HTML:
Как только у config$ будет значение, мы увидим его в HTML.
Теперь посмотрим список пользователей (containers/users/users.component.ts):
Мы отображаем список пользователей в компоненте презентации, посылая список и связывая выбранного пользователя с функцией навигации, которую можно увидеть в компоненте пользовательского контейнера выше.
Давайте посмотрим на компонент пользовательского контейнера:
Этот компонент получает идентификатор параметра из активированного маршрута, а с остальным вы, вероятно, уже знакомы, отправляет идентификатор в качестве параметра, выбирает выбранного пользователя…
Если вы отлаживаете приложение, вы можете увидеть инструменты разработчика, которые довольно просты в использовании… но в этой статье мы изучили достаточно, и я надеюсь, что вы без труда разберетесь с этими инструментами.
Заключение
В этой статье я попытался представить ясное и понятное введение в NGRX, предоставив вам все, что вам нужно знать, чтобы начать разработку используя этот инструмент.
Я очень надеюсь, что статья поможет тебе понять шаблон действий и рекомендую вам скачать его и немного поиграть с кодом.
Angular: понятное введение в NGRX
Цель этой статьи — дать чистое и ясное представление о ngrx. Для этого я объясню, что нужно знать и понимать о ngrx, а затем мы увидим это в действии с простыми и понятными примерами кода.
Вот список тем, которые мы будем обсуждать в этой статье:
Что такое NGRX
NGRX — это группа библиотек, «вдохновленных» шаблоном Redux, который, в свою очередь, «вдохновлен» шаблоном Flux. Проще говоря, это означает, что шаблон Redux является упрощенной версией Flux шаблона, а NGRX — angular/rxjs версией Redux шаблона.
Что я имею в виду под «angular/rxjs» версией redux… «angular» часть заключается в том, что ngrx — это библиотека для использования в приложениях angular. Часть «rxjs» заключается в том, что реализация ngrx работает вокруг потока rxjs. Это означает, что он работает с использованием наблюдаемых и различных наблюдаемых операторов, предоставляемых «rxjs».
Основной целью этой схемы является обеспечение предсказуемого состояния контейнера, основанного на трех основных принципах
Давайте рассмотрим три принципа модели Redux и укажем на наиболее важные преимущества, которые они дают.
Единственный источник правды
В одном хранилище? О хранилищах мы поговорим позже, но для общего понимания, они несут ответственность за сохранение состояния и применение к нему изменений, когда им говорят об этом (когда отправляется действие, мы также поговорим о них позже).
Преимущества наличия одного источника правды многочисленны, но для меня наиболее интересным (потому что это то, что будет влиять на любое angular приложение) является следующее:
Состояние read-only (только для чтения)
Отправить действие. Мы поговорим о действиях позже, но для общего понимания, это идентификаторы операции над вашим приложением, и они могут быть запущены (или отправлены), чтобы сообщить приложению выполнить операцию, которую представляет действие.
Избегая обновлять состояние из разных мест и имея централизованное место для внесения изменений, которое реагирует на конкретные действия, вы получаете много преимуществ. Не говоря уже о самых важных:
Изменения вносятся с простыми функциями
Операция, инициируемая отправкой действия, будет простой функцией, называемой в рамках архитектуры redux, редукторами.
Эти редукторы (помните, что они простые функции) получают действие и состояние, в зависимости от отправленного действия (обычно с оператором switch), они выполняют операцию и возвращают объект нового состояния.
Состояние в redux приложении является неизменным! Поэтому, когда редуктор что-то меняет в состоянии, он возвращает новый объект состояния.
Преимущества использования чистых функций хорошо известны, как, например, тот факт, что они могут быть немедленно протестированы, если вы передадите те же аргументы, которые вы получите в результате.
Этот подход также позволяет нам перемещаться между различными экземплярами нашего состояния с помощью инструментов разработки Redux/ngrx и видеть, что изменилось между экземплярами и кто его изменил, помимо всего прочего. Таким образом, использование чистых функций и возвращение новых экземпляров состояния также имеет большое влияние на отладку.
Но главное преимущество, на мой взгляд, заключается в том, что, привязав все входные данные наших компонентов к свойствам состояния, мы можем изменить стратегию обнаружения изменений на push, и это повысит производительность приложения.
Отлично… Итак, каковы преимущества использования NGRX?
Мы уже упоминали большинство из них, когда говорили о принципах redux шаблона. Но давайте отметим наиболее важные преимущества использования redux шаблона в приложении (на мой взгляд):
… и минусы
Когда стоит использовать NGRX
Таким образом, по общему мнению, ngrx следует использовать в средних/больших проектах, когда управление состоянием начинает становиться сложным в обслуживании. Некоторые люди, более фанатичные, скажут что-то вроде «если у вас есть состояние, то у вас есть NGRX».
Я согласен, что его следует использовать в средних или крупных проектах, когда у вас есть значительное состояние и множество компонентов, использующих это состояние, но вы должны учитывать, что Angular сам по себе предоставляет множество решений для управления состоянием, и если у вас есть сильная команда разработчиков Angular, то, возможно, вам не нужно беспокоиться о ngrx.
При этом я считаю, что сильная команда разработчиков Angular может также решить включить ngrx в решение, потому что они знают всю мощь redux шаблона, а также силу добавляемую операторами rxjs, и они чувствуют себя комфортно, работая с обоими…
Если вы ожидали простого ответа, чтобы решить, когда использовать ngrx, вы не получите его и не доверяете никому, кто дает вам этот ответ, за пределами вашей организации или группы. Решение зависит от изучения плюсов и минусов, понимания вашей команды и учета их мнения.
NGRX Действия, Редукторы, Селекторы, Хранилище, и Эффекты
Это основные строительные блоки потока ngrx. Каждый из них берет на себя часть процесса начала операции по изменению нашего состояния и получению данных.
На изображении мы видим поток ngrx. Давайте объясню его…
В объекте «Хранилище» имеется функция отправки (запуска) действий. Действия — это классы, реализующие интерфейс NGRX Action. У этих классов Action’ов есть два свойства (возьмем в качестве примера класс действия под названием GetUserName):
type (тип): это строка только для чтения, описывающая, что означает действие. Например: ‘[User] Get User Login».
payload (полезная нагрузка): тип этого свойства зависит от того, какие данные нужно отправить редуктору. В случае предыдущего примера это будет строка, содержащая имя пользователя. Не все действия должны иметь полезную нагрузку.
Редукторы — это простые функции, принимающие два аргумента: предыдущее состояние и Действие. При отправке Действие ngrx проходит через все редукторы, передающие в качестве аргументов предыдущее состояние и Действие, в том порядке, в котором редукторы были созданы, до тех пор, пока не найдет аргументы для этого действия.
Воздействие на экосистему библиотек ngrx позволяет нам иметь дело с побочными эффектами, вызванными отправкой действия за пределы угловых компонентов или хранилища ngrx.
Effects слушает, если какое-либо действие отправлено, то, подобно редукторам, он проверяет, является ли действие одним из тех типов действий, которые у него есть.
Затем выполняется побочный эффект, обычно получение или отправка данных в API.
В конце концов, редуктор будет выдавать еще одно действие, обычно относящееся к состоянию результата побочного эффекта (success, error и т.д.), затем редуктор войдет в сцену, о чем мы уже упоминали в потоке ngrx.
Как мы упоминали ранее, дерево состояний может стать довольно большим объектом, не имеет смысла размещать весь этот объект в тех местах, где нам нужна только его часть.
Хранилище NGRX предоставляет нам функцию «выбрать» для получения частей нашего хранилища. Но что, если нам нужно применить некоторую логику к этому срезу, прежде чем использовать данные в компонентах.
Здесь селекторы принимают меры. Они позволяют нам отделить любое преобразование данных среза состояния от компонентов. Функция выбора «store» принимает в качестве аргумента простую функцию, эта функция является нашим селектором.
Хранилище — это объект (экземпляр класса Store ngrx), который объединяет вещи, о которых мы упоминали ранее (действия, редукторы, селекторы). Например, когда отправляется действие (с использованием функции отправки объекта хранилища), хранилище является тем, которое находит и выполняет соответствующий редуктор.
Он также является держателем состоянием приложения.
Продолжение статьи с примерами использования: «Angular: пример использования NGRX».
Недавно я изучал Angular 6 с помощью @ ngrx / store, в то время как одним из руководств является использование @ ngrx / store для управления состоянием, , однако я не понимаю преимущества использования @ ngrx / store за кулисами.
Итак, вернемся к вопросу, почему мы используем @ ngrx / store вместо хранилища регистрации служб здесь, в проекте Angular? я знаю, что это для » УПРАВЛЕНИЯ ГОСУДАРСТВОМ «, но что именно такое «ГОСУДАРСТВЕННОЕ УПРАВЛЕНИЕ»? Это что-то вроде журнала транзакций и когда нам это нужно? Зачем нам управлять этим во внешнем интерфейсе? Пожалуйста, не стесняйтесь поделиться своим предложением или опытом в области @ ngrx / store!
3 ответа
Я думаю, что вы должны прочитать эти два поста о магазине Ngrx:
Если первый объясняет основные проблемы, решаемые Ngrx Store, он также цитирует это утверждение из React How-To, «которое в равной степени относится к оригинальному Flux, Redux, Ngrx Store или любому другому решению магазина в целом»:
Вы будете знать, когда вам нужен Flux. Если вы не уверены, нужно ли вам это, вам это не нужно.
Для меня магазин Ngrx решает несколько проблем. Например, когда вам приходится иметь дело с наблюдаемыми и когда ответственность за некоторые наблюдаемые данные распределяется между различными компонентами. В этом случае действия магазина и редуктора гарантируют, что изменения данных всегда будут выполняться «правильным образом».
Это также обеспечивает надежное решение для кэширования http-запросов. Вы сможете хранить запросы и их ответы, чтобы вы могли проверить, что на ваш запрос еще не сохранен ответ.
Во втором посте рассказывается о том, что привело к появлению таких решений в мире React с проблемой счетчика непрочитанных сообщений Facebook.
Относительно вашего решения по хранению необратимых данных в сервисах. Он отлично работает, когда вы имеете дело с постоянными данными. Но когда нескольким компонентам придется обновить эти данные, вы, вероятно, столкнетесь с проблемами обнаружения изменений и неправильными проблемами обновления, которые вы могли бы решить с помощью:
Если данные в вашем приложении используются в нескольких компонентах, то необходим какой-то сервис для обмена данными. Есть много способов сделать это.
Умеренно сложное приложение в конечном итоге будет выглядеть как интерфейсная структура внешнего интерфейса с обработкой данных, выполняемой в сервисах, с предоставлением данных через наблюдаемые компоненты.
В какой-то момент вам нужно написать какой-то API-интерфейс для ваших служб данных, как вводить и выводить данные, выполнять запросы и т. Д. Множество правил, таких как неизменность данных, и четко определенные одиночные пути для изменения данных. Не в отличие от серверной части, но гораздо быстрее и отзывчивее, чем вызовы API.
Ваш API будет выглядеть как одна из многих библиотек управления состоянием, которые уже существуют. Они существуют для решения сложных проблем. Они могут вам не понадобиться, если ваше приложение простое.
Прекратите использовать Ngrx/effects для этого
Иногда простейшая реализация функциональности в конечном итоге создает больше проблем, чем пользы, только увеличивая сложность в другом месте. Конечным результатом является забагованная архитектура, которую никто не хочет трогать.
Статья была написана в 2017 году, но актуальна и по сей день. Нацелена на людей опытных в RxJS и Ngrx, либо желающих попробовать Redux в Angular.
Фрагменты кода были обновлены исходя из текущего синтаксиса RxJS и немного модифицированы для улучшения читабельности и простоты понимания.
Ngrx/store — это библиотека Angular, которая помогает сдерживать сложность отдельных функций. Одна из причин заключается в том, что ngrx/store охватывает функциональное программирование, которое ограничивает то, что может быть сделано внутри функции, для достижения большей разумности вне ее. В ngrx/store такие вещи как reducers (далее редюсеры), selectors (далее селекторы) и операторы RxJS являются чистыми функциями.
Чистые функции проще тестировать, отлаживать, анализировать, распараллеливать и комбинировать. Функция чиста, если:
Побочных эффектов невозможно избежать, но они изолированы в ngrx/store, так что остальная часть приложения может состоять из чистых функций.
Побочные эффекты
Когда пользователь отправляет форму, нам нужно внести изменения на сервере. Изменение на сервере и ответ клиенту является побочным эффектом. Это может быть обработано в компоненте:
Было бы хорошо, если бы мы могли просто отправлять (dispatch) action (далее действие) внутри компонента, когда пользователь отправляет форму, и обрабатывать побочный эффект в другом месте.
Ngrx/effects — это middleware для обработки побочных эффектов в ngrx/store. Он прослушивает отправленные действия в потоке observable, выполняет побочные эффекты и возвращает новые действия немедленно или асинхронно. Возвращенные действия передаются в reducer.
Возможность обрабатывать побочные эффекты RxJS-способом делает код чище. После отправки начального действия SAVE_DATA из компонента вы создаете класс эффектов для обработки остальных:
Это упрощает работу компонента лишь до отправки действий и подписки на observable.
Легко злоупотребить Ngrx/effects
Ngrx/effects — очень мощное решение, поэтому им легко злоупотребить. Вот некоторые распространенные анти-паттерны ngrx/store, которые Ngrx/effects упрощает:
1. Дублированное состояние
Допустим, вы работаете над каким-то мультимедийным приложением, и у вас есть следующие свойства в дереве состояний:
Неверный ответ: используйте Ngrx/effects!
Правильный ответ: если состояние mediaPlaying полностью предсказывается другой частью дерева состояний, то это не истинное состояние. Это производное состояние. Это принадлежит селектору, а не store.
Теперь наше состояние может оставаться чистым и нормализованным, и мы не используем Ngrx/effects для чего-то, что не является побочным эффектом.
2. Сцепление действий c reducer
Представьте, что у вас есть эти свойства в вашем дереве состояний:
Неверный ответ: используйте Ngrx/effects!
Итак, теперь у нас будет два действия, отправляемых друг за другом, и два редюсера, возвращающие новые состояния друг за другом.
3. Запрос данных для компонента
Вашему компоненту нужны данные из store, но сначала их нужно получить с сервера. Вопрос в том, как мы можем поместить данные в store, чтобы компонент мог их получить?
Болезненный способ: используйте Ngrx/effects!
В компоненте мы инициируем запрос, отправив действие:
В классе эффектов мы слушаем GET_USERS :
Теперь предположим, что пользователь решает, что определенный route грузится слишком много времени, поэтому он с него переходит на другой. Чтобы быть эффективными и не загружать ненужные данные, мы хотим отменить этот запрос. Когда компонент будет уничтожен, мы отменим подписку на запрос, отправив действие:
Теперь в классе эффектов мы слушаем оба действия:
Лучшая помощь, которую я нашел для этого конкретного сценария, — это этот отличный пост. В его примере callApiY требует, чтобы callApiX уже был завершен. Я убрал комментарии, чтобы это выглядело менее пугающе, но не стесняйтесь читать оригинальный пост, чтобы узнать больше:
Теперь добавьте требование, что HTTP-запросы должны быть отменены, когда компоненты в них больше не нуждаются, и это станет еще более сложным.
Итак, почему так много проблем с управлением зависимостями данных, когда RxJS должен делать это действительно легко?
Хотя данные, поступающие с сервера, технически являются побочным эффектом, мне не кажется, что Ngrx/effects — лучший способ справиться с этим.
Компоненты — это интерфейсы ввода/вывода для пользователя. Они показывают данные и отправляют действия, произведенные им. Когда компонент загружается, он не отправляет никаких действий, произведенных этим пользователем. Он хочет показать данные. Это скорее похоже на подписку, нежели на побочный эффект.
Очень часто можно увидеть приложения, использующие действия для инициации запроса данных. Эти приложения реализуют специальный интерфейс для observable через побочные эффекты. И, как мы видели, этот интерфейс может стать очень неудобным и громоздким. Подписываться на, отписываться от и связывать сами observable гораздо проще.
Менее болезненный способ: компонент зарегистрирует свою заинтересованность в данных, подписавшись на них через observable.
Мы создадим observable, которые содержат нужные HTTP-запросы. Мы увидим, насколько проще управлять несколькими подписками и цепочками запросов, зависящими друг от друга, используя чистый RxJS, нежели делать это через эффекты.
Создадим эти observable в сервисе:
Поскольку эта зависимость данных теперь простой observable, мы можем подписаться и отписаться в шаблоне, используя async pipe, и нам больше не нужно отправлять действия. Если приложение уходит с роута последнего компонента, подписанного на данные, HTTP-запрос отменяется или веб-сокет закрывается.
Цепочку зависимостей данных можно обрабатывать так:
Вот параллельное сравнение вышеупомянутого метода с этим методом:
Заключение
Ngrx/effects — прекрасный инструмент! Но рассмотрите эти вопросы, прежде чем использовать его: