Рубрики
Без рубрики

Простое, но мощное государственное управление в угловом углу с RXJS

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

TLDR Давайте создадим наш собственный класс управления государственным управлением только с RXJS/поведением (вдохновленный некоторыми известными государственными менеджментами).

Управлять состоянием с RXJS поведение

Там есть несколько великолепных государственных библиотек по управлению там для управления состоянием в угловом углу: например. Ngrx, Akita или NGXS. У всех нас есть одно общее: они основаны на наблюдаемых RXJS, и состояние хранится в особом виде наблюдаемого: поведение.

Почему rxjs наблюдается?

  • Наблюдается, что граждане первого класса в угловых. Многие из основных функций угловых имеют реализацию RXJS (например httpClient, Forms, маршрутизатор и более). Управляющее состояние с наблюдателями приятно интегрируется с остальной частью угловой экосистемы.
  • С наблюдателями легко информировать компоненты о государственных изменениях. Компоненты могут подписаться на наблюдаемые, которые удерживают состояние. Эти «государственные» наблюдаемые заметные выделяют новое значение при изменении государства.

Что особенное в поведении поведение?

  • Поведение преобразования испускает последнее значение для новых/поздних подписчиков
  • Имеет первоначальное значение
  • Его текущее значение может быть доступна через GetValue метод
  • Новое значение может быть выпущено с помощью Следующий метод
  • Поведение – это многоадресная передач: внутренне он содержит список всех подписчиков. Все подписчики разделяют одно и то же наблюдаемое исполнение. Когда поведение введение испускает новое значение, то то же самое значение нажата ко всем подписчикам.

Наше собственное государственное управление с поведением

Поэтому, если все большие государственные Libs Management используют RXJS поведение поведение и угловой поставляется с RXJS из коробки … Можем ли мы создать наше собственное государственное управление с лишним угловым сервисом и поведение?

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

Ключевыми целями являются:

  • Быть в состоянии определить интерфейс состояния и установить начальное состояние
  • Прямой API для обновления состояния и выберите Состояние: SetState , Выбрать
  • Выбранное состояние должно быть возвращено как наблюдаемое. Наблюдаемые излучают, когда выбранные изменения состояния.
  • Быть в состоянии использовать CENSEDETICEGRATEGY. Onpush В наших компонентах для лучшей производительности (Читайте больше на onpush здесь: “Комплексное руководство по угловой стратегии обнаружения изменения onpush” ).

Решение:

import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';

export class StateService {
  private state$: BehaviorSubject;
  protected get state(): T {
    return this.state$.getValue();
  }

  constructor(initialState: T) {
    this.state$ = new BehaviorSubject(initialState);
  }

  protected select(mapFn: (state: T) => K): Observable {
    return this.state$.asObservable().pipe(
      map((state: T) => mapFn(state)),
      distinctUntilChanged()
    );
  }

  protected setState(newState: Partial) {
    this.state$.next({
      ...this.state,
      ...newState,
    });
  }
}

Давайте посмотрим на код выше:

  • Установка информирования ожидает общий тип T Представление интерфейса состояния. Этот тип передан при расширении государственного обслуживания.
  • Получить штат () Возвращает текущий государственный снимок
  • Конструктор принимает начальное состояние и инициализирует поведение.
  • Выберите принимает функцию обратного вызова. Эта функция называется, когда состояние $ испускает новое состояние. В пределах RXJS карта Функция обратного вызова вернет кусок состояния. Разноцветные Будет пропустить выбросы, пока выбранная часть состояния не содержит новую ссылку на объект. Это .state $ .Asobsibable () Убедитесь, что Выберите Метод возвращает наблюдаемый (а не анонимсубъектировать ).
  • SetState принимает частичный тип. Это позволяет нам лениться и проходить только некоторые свойства более крупного интерфейса состояния. Внутри состояние $ .Next Метод частичного состояния объединяется с полным состоянием объекта. Наконец поведение поведение Это .state $ выделяет совершенно новый государственный объект.

Применение

Угловые сервисы, которые должны управлять каким-либо состоянием, могут просто продлить инверсию для выбора и обновления и обновления.

В мире есть только одна вещь, чтобы управлять: Тодос!:) Давайте создадим тодосстатесвис.

interface TodoState {
  todos: Todo[];
  selectedTodoId: number;
}

const initialState: TodoState = {
  todos: [],
  selectedTodoId: undefined
};

@Injectable({
  providedIn: 'root'
})
export class TodosStateService extends StateService{
  todos$: Observable = this.select(state => state.todos);

  selectedTodo$: Observable = this.select((state) => {
    return state.todos.find((item) => item.id === state.selectedTodoId);
  });

  constructor() {
    super(initialState);
  }

  addTodo(todo: Todo) {
    this.setState({todos: [...this.state.todos, todo]})
  }

  selectTodo(todo: Todo) {
    this.setState({ selectedTodoId: todo.id });
  }
}

Давайте пройдемся через код TodosStateService:

  • ToSstateService расширяет Условия использования и передает интерфейс состояния Тодость
  • Конструктор должен позвонить Super () и пройти начальное состояние
  • Общественные наблюдаемые TODOS $ и ВыбратьTodo $ подвергать соответствующие данные состояния для заинтересованных потребителей, таких как компоненты или другие услуги
  • Общественные методы Addtodo и SelectTodo выдержать публичное API для обновления состояния.

Взаимодействие с компонентами и Backend API

Давайте посмотрим, как мы можем интегрировать наш тодосстатеждервис с угловыми компонентами и API Backend:

  • Компоненты называют публичными методами ToSstateService для обновления состояния
  • Компоненты, заинтересованные в штате, просто подписываются на соответствующие публичные наблюдаемые, которые выставляются в ToSstateService.
  • Вызывы API тесно связаны с состоянием. Довольно часто API-ответ будет напрямую обновлять состояние. Поэтому вызовы API запускаются ToSstateService. После завершения вызова API государство может быть обновлено сразу, используя setstate.

Демонстрация

Смотрите полный приложение Todos Todos, используя ToSstateService: Stackblitz – угловой государственный менеджер

Примечания

Неизменные данные

Чтобы извлечь выгоду из CENSEDETICEGRATEGY. Onpush В наших компонентах мы должны убедиться, что не мутируют государство. Наша обязанность всегда передавать новый объект к SetState метод. Если мы хотим обновить вложенное свойство, которое содержит объект/массив, то мы должны также назначить новый объект/массив.

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

FYI Есть libs, которые могут помочь вам сохранить данные состояния неизменными: Иммер Immutablejs.

Формы ведомых шаблонов с двусторонним связыванием данных

Что касается неизменных данных … Мы должны быть осторожны при точке состояния в форму на основании шаблона, где используются входы формы [(NGModel)] Отказ Когда пользователь меняет входное значение формы, то состояние состояния будет мутирован напрямую … Но мы хотели остаться неизменными и изменять состояние, только явно используя SetState Отказ Поэтому это лучшая альтернатива использовать реактивные формы. Если он должен быть формы на основе шаблонов, то еще есть хороший компромисс: односторонние данные, связывающие [NGModel] Отказ Другой вариант должен (глубоко) клонировать данные формы … В таком случае вы все еще можете использовать [(NGModel)] Отказ

Асинхронная труба для подписок

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

Существует еще одна польза от Async Pipe: когда компоненты используют стратегию обнаружения изменений onPush, они будут обновлять свое мнение только в этих случаях автоматически:

  • Если @Input получает новую ссылку на объект/объект
  • Если событие DOM срабатывает от компонента или одного из его детей

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

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

@Component({
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class TodoShellComponent {
  todos: Todo[];

  constructor(
    private todosState: TodosStateService,
    private cdr: ChangeDetectorRef
  ) {
    this.todosState.todos$.subscribe(todos => {
      this.todos = todos;
      this.cdr.markForCheck(); // Fix View not updating
    });
  }
}

Но мы также можем использовать async Труба в шаблоне вместо этого. Это призывает CENTERVETECTREF.MORKFORCHECK для нас. Смотрите здесь, в угловом источнике: async_pipe.

Намного короче и красивее:


Асинхронная труба много делает. Подписаться, Отписаться, MarkForcheck. Давайте использовать это, где это возможно.

Смотрите асинхронный труб в действии в демонстрации: todo-shell.component.html.

Выбор обратных вызовов часто называется

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

Multicast исчезли

Если есть много подписчиков для наблюдаемого, которая возвращается Выберите Метод тогда мы видим что-то интересное: многоядерку поведение поведение прошло … Функция обратного вызова передана на Выберите Метод называется несколько раз при изменении состояния. Наблюдаемый выполнен на подписчик. Это потому, что мы преобразовали поведение поведение на наблюдаемое использование Это .state $ .Asobsibable () Отказ Наблюдаемые не многоадресные.

К счастью, RXJS предоставляет (многоадрессионный) оператор, чтобы сделать наблюдаемые многоадресные: SharerePlay Отказ

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

todos$: Observable = this.select(state => state.todos).pipe(
    shareReplay({refCount: true, bufferSize: 1})
);

Важно использовать RefCount: True Чтобы избежать утечек памяти. Буферизация: 1 Убедитесь, что поздние подписчики все еще получают последнее исходное значение.

Узнайте больше о многоадресных операторах здесь: Магия операторов обмена RXJS и их различия

Фасадный рисунок

Есть еще одна хорошая вещь. Государственная служба управления способствует фасадный рисунок : Выбрать и SetState Охраняемые функции. Поэтому они могут быть вызваны только внутри ТодосстатесСервис Отказ Это помогает поддерживать компоненты наклоняться и чистыми, поскольку они не смогут использовать SetState / Выберите Методы напрямую (например, на инъецированном тодосстатеревице). Государственные детали реализации остаются внутри ТодосстатесСервис. Шаблон фасада позволяет легко рефакторизоровать ToSstateService в другое государственное решение для управления (например, Ngrx) – если вы когда-нибудь хотите:)

Спасибо

Особое спасибо за рассмотрение этого блога:

Статьи, которые вдохновили меня:

Оригинал: “https://dev.to/angular/simple-yet-powerful-state-management-in-angular-with-rxjs-4f8g”