Ааа сегодня мы разберем зачем мемоизировать селекторы редакса и почему автору стейт-менеджера (СТМ) хорошо бы знать виды графов и алгоритмы на них.
Давайте представим следующую модель данных, в корзине товаров у нас отображаются:
- цена
- сумма налога (зависит от цены)
- общая стоимость (зависит от цены и налога)
(пример максимально простой намеренно, в реальном приложении описанные ниже проблемы будут серьёзнее)
Все это, конечно, должно быть связано реактивно, как показано на рисунке (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) ...
И тут мы видим что "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…