Угловой

Введение в NgRx

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

Если вы слышали о NgRx, вы, вероятно, думали, что он сложный и что вам не следует его использовать, если ваше приложение не поддерживает сложное состояние.

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

Это введение в основные понятия NgRx.

начну с ответа на

  • Что такое NgRx?
  • Зачем нам нужно государственное управление?

прежде чем приступить к созданию простого приложения, использующего NgRx.

Что такое NgRx?

NgRx — это библиотека управления состоянием, использующая шаблон Redux.

Что такое государство?

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

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

Это хранилище — единственный источник достоверной информации для всего состояния приложения.

Эта концепция исходит от Redux.

Что такое Редукс?

Redux — это шаблон управления состоянием и библиотека для реализации этого шаблона в любом приложении.

Основная идея Redux заключается в том, что состояние всего приложения хранится в одном месте: в хранилище.

Думайте о магазине как об объекте JavaScript.

Зачем нам нужно государственное управление?

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

Но для этого мы могли бы использовать услуги! Для небольших и простых приложений сервисов достаточно для обработки состояния приложения.

В частности, используя RxJS и Subjects, мы можем довольно далеко продвинуться без использования NgRx.

Тем не менее, есть несколько веских причин для использования управления состоянием и, в частности, NgRx, если вы используете Angular.

Причины использовать NgRx

Согласно ngrx.io, «можно использовать NgRx

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

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

Они даже предлагают удобное руководство, отвечающее на вопрос: нужен ли мне NgRx Store?

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

Почему недостаточно услуг?

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

Более того:

  • Angular может не обнаруживать изменения значений свойств, вложенных в объекты, при использовании RxJS и Subjects. В JavaScript изменение свойства объекта не меняет всего объекта.
  • Вам нужен четкий и структурированный шаблон для обновления данных в вашем приложении.

При этом имейте в виду, что Магазин NgRx требует некоторых компромиссов […]. Это не самый короткий или самый быстрый способ написания кода. Это также поощряет использование большого количества файлов, ngrx.io.

Почему NgRx вместо Redux?

Вкратце, NgRx лучше интегрируется с Angular. Кроме того, он использует инъекционные сервисы, RxJs и TypeScript.

NgRx придерживается мнения о побочных эффектах, в то время как у Redux другие подходы.

Создайте простое приложение с помощью NgRx: обзор и теория

Мы создадим простое приложение-счетчик, чтобы изучить основы NgRx.

Это приложение во многом следует руководству, доступному на ngrx.io. Тем не менее, я добавил некоторые пояснения, которые могут помочь разработчикам, впервые использующим NgRx и шаблон Redux.

Начнем с создания функции счетчика в AppComponent. CSS в основном отсутствует, но вы можете найти весь код на GitHub.

Код в app.component.html

<p>Current Count</p>
<h2>{{ count }}</h2>
<button (click)="increment()">+</button>
<button (click)="decrement()">-</button>
<button (click)="reset()">Reset</button>

Код в app.component.ts

...
export class AppComponent {
  title = 'ngrx';
  count: number = 0;
  increment() { this.count++; }
  decrement() { this.count--; }
  reset() { this.count = 0; }
}

Жизненный цикл локального состояния

На данный момент AppComponent имеет локальное состояние. Это переменная с именем counter, в ней хранится значение счетчика.

Давайте кратко рассмотрим жизненный цикл состояния в AppComponent.

Начиная с шаблона:

  1. нажатие на кнопку вызывает событие клика
  2. событие click выполняет метод
  3. метод обновляет значение counter в классе
  4. наконец, шаблон отражает обновленное значение благодаря интерполяции строк

Общий общий поток состояния приложения в NgRx довольно сложен, но некоторые элементы похожи на то, что я только что описал выше.

Жизненный цикл глобального состояния

В NgRx компонент не хранит состояние и не управляет изменениями, когда что-то происходит.

Состояние теперь хранится в хранилище, а не в компоненте.

Давайте посмотрим, как меняется жизненный цикл состояния, начиная с шаблона:

  1. нажатие на кнопку вызывает событие клика
  2. событие click выполняет метод
  3. метод отправляет действие редюсеру
  4. редуктор выполняет логику для обновления значения counter в хранилище
  5. наконец, шаблон отражает обновленное значение благодаря интерполяции строк

Мы можем разбить эту логику еще дальше.

  1. Обновить состояние. Первые четыре шага обновляют значение состояния в хранилище.
  2. Потяните состояние. Последний шаг извлекает значение состояния из хранилища.

Далее мы начинаем создавать приложение, следуя этим двум частям.

Создайте простое приложение с помощью NgRx: Код

Прежде всего, установите NgRx

npm install @ngrx/store --save

Затем давайте поработаем над частью 1, чтобы обновить значение состояния в хранилище.

Часть 1. Обновление состояния

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

Первые два шага такие же, но третий шаг вводит две новые концепции: действия и редюсеры.

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

Давайте отправим действие редюсеру.

Создать действие

Я создал папку состояния, куда буду включать все, что связано с управлением состоянием. Внутри папки состояния я создал файл с именем counter.actions.ts. Существуют и другие способы структурирования NgRx, но это самый простой способ начать.

Я создаю действие для увеличения счетчика:

//  counter.actions.ts
import { createAction } from '@ngrx/store';
export const increment = createAction('[App Component] Increment');

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

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

Создать редуктор

В папке состояния я создал файл с именем counter.reducer.ts.

//  counter.reducer.ts

import { createReducer, on } from '@ngrx/store';
import { increment } from './counter.actions';
export const initialState = 0;
export const counterReducer = createReducer(
  initialState,
  on(increment, (state) => state + 1),
);

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

Для создания редуктора мы используем метод createReducer.

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

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

Импорт NgRx в AppModule

Поскольку мы не использовали Angular CLI для добавления NgRx, нам нужно добавить его вручную.

В AppModule мы импортируем:

// app.module.ts
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './state/counter.reducer';
@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, StoreModule.forRoot({ count: counterReducer })],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Обратите внимание, как «метод StoreModule.forRoot() регистрирует глобальных провайдеров, необходимых для доступа к Store через ваше приложение», ngrx.io.

Функция StoreModule.forRoot принимает объект, содержащий count, и метод counterReducer, который мы используем для управления состоянием счетчика.

Отправка действия редюсеру

Теперь у нас есть действие (инкремент) и редюсер (counterReducer). Давайте, наконец, отправим наше первое действие.

Как мы сказали выше, метод отправляет действие редюсеру, поэтому давайте обновим код в app.component.ts для отправки действий.

import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { increment } from './state/counter.actions';
...
export class AppComponent {
  title = 'ngrx';
  count: number = 0;
  constructor(private store: Store) {}
  increment() { 
    this.store.dispatch(increment());  
  }
  decrement() { this.count--; }
  reset() { this.count = 0; }
}

Обратите внимание, что нам нужно импортировать Store и increment. Затем мы внедряем store в конструктор и, наконец, обновляем метод increment:

increment() { 
  this.store.dispatch(increment());  
}

Теперь, если вы нажмете на кнопку + в приложении, то увидите, что ничего не происходит.

Однако логика верна. Мы отправляем действие редюсеру, и редьюсер обновляет состояние.

Вы можете проверить это с помощью Angular DevTools. Смотрите скриншот ниже.

В Angular DevTools мы видим три свойства: count, store и title.

  • количество. Значение count равно 0. Это имеет смысл, поскольку мы инициализировали count значением 0 в AppComponent. Поскольку мы изменили метод incrementдля использования диспетчера, ничто больше не меняет значение countany.
  • магазин. Когда мы смотрим на значение store/source/_value/count, мы видим, что оно равно 6. Это значение count в состоянии в хранилище! Я шесть раз нажал кнопку + и вот результат!
  • название. Это просто строка «ngrx»

Итак, состояние обновляется, и на этом завершается Часть 1: обновление состояния.

Почему мы не видим его в пользовательском интерфейсе? Потому что шаблон получает значение из count в app.component.ts, а не из магазина! Итак, часть 2. Вытяните состояние.

В качестве примечания, есть расширение Redux, которое дает вам некоторое представление об управлении состоянием NgRx.

Часть 2. Вытяните состояние

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

Для получения данных из хранилища мы используем селектор, метод select, который возвращает поток текущего состояния.

Код в app.component.ts развивается до:

...
import { Observable } from 'rxjs';
...
export class AppComponent {
  title = 'ngrx';
  count: number = 0;  // remove once all methods dispatch actions
  count$: Observable<number>;
  constructor(private store: Store<{ count: number }>) {
    this.count$ = this.store.select('count');
  }
  ...
}

Обратите внимание, что count$ относится к наблюдаемому потоку. Мы инициализируем значение count$ в конструкторе.

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

Поэтому app.component.html становится:

...
<h2>{{ count$ | async }}</h2>
...

Теперь пользовательский интерфейс должен обновляться каждый раз, когда вы нажимаете на +.

Другие кнопки не дадут никакого эффекта. необходимо

  • создать действие,
  • обновить редуктор,
  • отправить действие редюсеру

для каждой кнопки.

Не стесняйтесь попробовать его самостоятельно или найти код на GitHub.

Заключительные соображения

Это просто введение в NgRx, и оно не исследует эффекты.

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

Несмотря на эти ограничения, я надеюсь, что этот пост помог вам лучше понять NgRx.

Ключевые идеи

  • NgRx — это библиотека управления состоянием, использующая шаблон Redux.
  • Состояние — это объект JavaScript, который содержит данные, используемые в вашем приложении.
  • Redux — это шаблон управления состоянием, который выступает за хранение состояния приложения в одном центральном месте: хранилище.
  • Чтобы обновить состояние, вам нужно создать действие, обновить редюсер и отправить действие редюсеру.
  • Чтобы вытащить данные из состояния, вам нужно использовать метод select.

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь: