Pull to refresh

Comments 17

Мне как-то комфортнее на mobx. Пробовал redux — так себе удовольствие. Постоянное копирование state, особенно в крупном проекте, больно бьет по производительности

Если речь все еще об Angular — попробуйте ngxs.

Как понимаю концепция таже, только rxjs в помощь. Mobx выглядит легковеснее. Представление сам понимает при изменении каких observable и computed свойств требуется перерисовка
Судя по документации, в NGXS присутствуют все проблемы Redux-подобных сторов: неудобная иммутабельность, необходимость вручную описывать мемоизированные селекторы, обилие бойлерплейт кода. Вот пример из документации:

export class FeedZebra {
  static readonly type = '[Zoo] FeedZebra';
  constructor(public zebraToFeed: ZebraFood) {}
}

...

@Action(FeedZebra)
feedZebra(ctx: StateContext<ZooStateModel>, action: FeedZebra) {
  const state = ctx.getState();
  ctx.patchState({
    zebraFood: [
      ...state.zebraFood,
      action.zebraToFeed,
    ]
  });
}

И это всё нужно для того, чтобы запушить значение в массив? На Mobx будет так:

@action      
feedZebra(zebraToFeed: ZebraFood) {
  this.zebraFood.push(zebraToFeed)
}


Mobx в каждом observable значении (в нашем случае zebraFood) хранит список слушателей, которые будут отработать при изменении этого значения. Слушателями будут выступать компоненты, которые рендерят этот observable. На выходе очень малое количество кода и прозрачный принцип работы. Чуть более подробно о недостатках Redux-подобных сторов, писал тут: habr.com/ru/company/inobitec/blog/481288/#comment_21052464

Конечно, не для того, чтобы запушить в массив. Redux-подобные сторы реализуют паттерн CQRS, с четким разделением запросов и команд. Обработка action-а может быть сколь угодно нетривиальной. У меня, например, был проект, где фронтенд взаимодействовал с rest api и двумя websocket api, без централизованного стейта и потока actions-reducers это бы все легко превратилось в один большой race condition. И возможность просто "отреплеить" экшены очень пригодилась (в первом приближении это что-то похожее на реплей стрима на youtube вместе с чатом). Там, правда, был ngrx (ngxs тогда еще не было, да и ngrx только появился); ngxs, мне кажется, более естественно вписывается в angular.


Для простых проектов согласен, что mobx уместнее — там добавляемая CQRS сложность не "окупится". Хотя к этой магии с обертками у меня личная неприязнь со времен knockout, но это субъективное :-)

Я как-то наоборот думаю). Что именно в крупных проектах mobx удобнее). Как раз к очень крупному проекту применяем. До этого redux пробовали, было много боли и страданий. В мелких проектах, где модель скорее всего простая, я бы применил redux или подобное. Инициализация активной модели mobx забирает больше времени, чем легковесный redux. Только ради этого. Но когда модель большучая, то иммутабельность просто уничтожает производительность
пришел точно к такому же мнению, пример большого проекта, однокласники, недавно тут была их архитектура приложения

Речь не о размере, а о сложности взаимодействий, модель-то там была относительно небольшая.


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

Akita вписывается в Angular еще лучше

Спасибо за статью, но на мой взгляд, отсутствие примеров с кодом не делает это введение понятнее.

Часть статьи с примером использования, пока находиться в написании, но в скором времени будет доступна.
Мне казалось, что все уже плюются от Redux и используют его только в силу невозможности переписать проект. Это ж тормозное нечно, тянущее за собой кучу однотипных и ненужных «переиспользований кода ровно один раз».
У меня сейчас на проекте такая архитектура, люди которые это придумали, надеюсь для вас есть отдельный котел! это самое отвратительное решение которое я видел! Angular из коробки умеет держать состояние, для этого достаточно сделать State Injectable, и если вам нужно отслеживать изменение переменно в коде то можно сделать декоратор
import { BehaviorSubject, Observable } from 'rxjs';

export function observable() {
    return <T>(target: any, propertyName: string) => {

        Object.defineProperty(target.constructor.prototype, `${propertyName}$`, {
            get(): Observable<T> {
                if (!this[`$$_${propertyName}`]) {
                    this[`$$_${propertyName}`] = new BehaviorSubject(undefined);
                }

                if (!this[`$$_${propertyName}$`]) {
                    this[`$$_${propertyName}$`] = this[`$$_${propertyName}`].asObservable();
                }
                return this[`$$_${propertyName}$`];
            }
        });

        Object.defineProperty(target.constructor.prototype, propertyName, {
            get(): T {
                return this[`$$_${propertyName}$`].source.getValue();
            },
            set(value: T) {
                if (this[`${propertyName}$`]) {
                    this[`$$_${propertyName}`].next(value);
                }
            }
        });
    };
}


После чего получить стейт
@Injectable()
class AppState {

userId$: Observable<number>;

@observable()
userId: number;
}


Затем в компонентах можно просто слушать как обычный Observable
в 90% моих случаев мне достаточно получить текущие значение из стейта и не отслеживать когда оно изменилось, а каждый раз обращаться в .source.getValue(), не очень охото, но это уже вкусовщина, а так да, вполне можно обойтись простым BehaviourSubject
В 90% случаев вообще не нужно получать значение, а нужно получить обсервабл и использовать его в шаблоне.
И не надо задумываться отслеживаем мы изменения или нет. Всегда отслеживаем.
Sign up to leave a comment.

Articles