🔥

Тред (@art_al_ar)


Ааа сегодня мы разберем зачем мемоизировать селекторы редакса и почему автору стейт-менеджера (СТМ) хорошо бы знать виды графов и алгоритмы на них.

Давайте представим следующую модель данных, в корзине товаров у нас отображаются: - цена - сумма налога (зависит от цены) - общая стоимость (зависит от цены и налога) (пример максимально простой намеренно, в реальном приложении описанные ниже проблемы будут серьёзнее)
notion image

Все это, конечно, должно быть связано реактивно, как показано на рисунке (push-подход) - update обновляет только price, дальше изменения "растекаются" автоматически. Иначе, обновляя каждое значение императивно внутри update (pull-подход) мы сильно повышаем зацепленность.

Цветами разделены необходимые этапы, выполнение которых должно произойти при поступлении нового значения: 1. (зеленый) - установка новых значений 2. (желтый) - вычисление зависимых значений 3. (красный) - вызов сайд-эффектов

Классические реактивные подходы основанные на потоках (observable) с которым можно познакомится тут habr.com/ru/post/279715/ или подробнее тут github.com/kriskowal/gtor

Но с этим стандартным и столь распространенным подходом есть достаточно серьёзная проблема - glitches, одна сторона которого подробно описана тут staltz.com/rx-glitches-ar… Я бы, вкратце, описал это так: обход графа подписок осуществляется в глубину, а надо бы в ширину.

На нашем примере стандартная последовательность оповещения observable будет выглядеть так: update(1) -> price(1) -> tax(1) -> cost(1) -> cost subscriber(1) -> tax subscriber(1) -> cost(2) -> cost subscriber(2) -> price subscriber(1) -> update subscriber(1) ...
notion image

И тут мы видим что "cost" и "cost subscriber" были вызваны дважды, что содержит целых две проблемы: лишние вычисления и в первом случае данные были неконсистентны, потому что cost содержал новое значение tax, но старое значение price.

Так же не верным считается вызов сайд-эффектов (subscribers) в процессе вычисления зависимых значений , почему - разберем завтра, это отдельная большая тема (обработка ошибок, атомарность и транзакционность).

Правильный порядок обхода подписок должен был бы выглядеть так: update -> price -> tax -> cost (сразу с новыми значениями price и cost) -> update subscriber -> price subscriber -> tax subscriber -> cost subscriber ->

Что бы достичь этого следует понимать что все зависимости - это определенный граф (DAG чаще всего en.wikipedia.org/wiki/Directed_…) и что его обход должен осуществляться в ширину (как описано в сообщении выше).

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

Вот тут есть немного более детальное сравнение (в тестах) эффектора, мобыкса и Rx codesandbox.io/s/effector-com…