Где лучше располагать вычисления суммы изменяющихся значений: в UI или в слое логики
Что проверяют
Понимаете ли вы разницу между исходным состоянием и производными данными и где проходит граница между компонентом и бизнес-логикой. Следом часто спрашивают про мемоизацию: когда useMemo или селектор реально нужны, а когда это лишний код.
Как отвечать
Главная мысль: сумма это производное значение, её не хранят, а вычисляют из источника истины. Отдельный стейт total, который обновляется через useEffect после изменения списка, даёт лишний рендер и шанс рассинхрона.
Дальше разделите два случая:
- сумма как правило предметной области (итог корзины, скидки, налог): чистая функция в слое модели, например
calcCartTotal(items, promo). Её легко покрыть юнит-тестами без рендера, и её переиспользуют разные экраны, серверный код, письма; - сумма как деталь отображения (сколько строк выбрано, итог в футере таблицы): её можно считать прямо в компоненте во время рендера.
Инструменты по стеку:
- React: вычисление в теле компонента,
useMemoтолько если расчёт действительно тяжёлый. React Compiler снимает часть ручной мемоизации; - Redux Toolkit: селекторы через
createSelector, результат пересчитывается только при смене входных данных; - Vue, MobX, Angular:
computed, в Angular это сигналы сcomputed().
Если значения меняются очень часто (котировки по WebSocket, тысячи строк), пересчёт всей суммы на каждое событие дорог. Тогда агрегат держат инкрементально: вычли старое значение, прибавили новое, а обновления интерфейса собирают в пачки через requestAnimationFrame.
И обязательная оговорка: для денег финальную сумму считает сервер, клиентская цифра лишь предпросмотр.
Где спотыкаются
Складывают деньги в числах с плавающей точкой: 0.1 + 0.2 не равно 0.3. Суммы держат в копейках целыми числами или считают через библиотеку десятичной арифметики.
Оборачивают в useMemo каждое сложение трёх чисел. Сравнение зависимостей стоит дороже самого расчёта, а код читается хуже.
Размазывают формулу по нескольким компонентам: в карточке товара одна логика скидки, в итоге корзины другая, и через месяц цифры расходятся.