# Proposal: мемоизация ячеек таблицы листа (AURORA-1839, Gitea #234) ## Why Скролл листа формы — главная жалоба заказчика (perf/REPORT.md, М1). До цепочки P1 шаг колеса 200 px на FORM_2/AHR стоил 89,0 мс главного потока (масштаб 1, rf, CPU 1×, медиана; `perf/report-data/20261026-s1/summary.md`). На профилирующей сборке это 3,3 коммита React и 14,8 тыс. рендеров компонентов на шаг, из них 1 696 `Styled(td)` (`analysis/hotspots.md`). Все ~500 видимых ячеек перерисовываются при каждом коммите. Дорого не собственная ячейка `Cell` (она в `React.memo`), а обёртка material-react-table: `MRT_TableBodyCell` → `TableCell` → `Styled(td)`. AURORA-1836 убрала чтение layout в `measureElement`, AURORA-1837 перенесла статичные стили ячеек в CSS контейнера. Обе задачи удешевили одну перерисовку ячейки, но не уменьшили число перерисовок. Число перерисовок сокращает только мемоизация ячеек MRT (`memoMode: 'cells'`). Без неё критерий эпика AURORA-1834 (≤ 25 мс на шаг) недостижим. С мемоизацией появляется риск устаревшего вида ячеек. MRT сравнивает только идентичность объекта `cell`, поэтому состояние вне данных строки (закрепление колонок, фильтры) нужно явно доносить до ячеек. Это ограничение распространяется на все будущие правки таблицы (AURORA-1889, 1890, 1891, 1893, 1910), поэтому его нужно записать как контракт. ## What Changes - Таблица листа (`RealtimeTable`) включает мемоизацию ячеек MRT (`memoMode: 'cells'`). Ячейки, которые остаются на экране при прокрутке, не перерисовываются. - Мемоизация сбрасывается явно, новым массивом колонок таблицы: - при изменении раскладки закреплённых колонок: закрепление, открепление, скрытие или показ закреплённой колонки, смена её ширины; - при изменении глобального поиска и фильтров по колонкам. Сброс не полагается на побочные эффекты MRT и `filterFromLeafRows`. - Ширина закреплённой колонки сбрасывает мемоизацию один раз, по отпусканию мыши. Пока колонку тянут, соседние закреплённые колонки могут отставать. Ширина незакреплённых колонок меняется через CSS-переменные MRT, их скрытие и показ мемоизацию не сбрасывают. - Собственное перетаскивание ширины в заголовке (`TableHead`/`ColumnResizer`) начинает сообщать MRT о начале и конце перетаскивания (`columnSizingInfo.isResizingColumn`). Фильтры по колонкам становятся управляемым состоянием `RealtimeTable`. - Записывается контракт для всех будущих правок таблицы: - рендер ячейки зависит только от объекта `cell`; - остальное состояние передаётся через CSS по атрибутам, контекст или хранилище с подпиской; - обработчики читают актуальное состояние через `ref` или `table.getState()`. - Добавляются node-тесты ключа сброса и допущения о пересоздании ячеек в `@tanstack/table-core`. - В стенд `perf/e2e` добавляется сценарий «закрепить колонку и потянуть её ширину — время кадра». - Пороги производительности (≤ 25 мс на шаг и др.) — цель, а не условие слияния. PR вливается по зелёным тестам и ручному регрессу, цифры замеров (контроллер, Apple M4) дописываются в PR. Недобор — follow-up issue с разбором. Корректность — жёсткое условие. - Поведение для пользователя не меняется: поиск, фильтры, ошибки валидации, блокировки соавторов, выделение строки, редактирование, разрядность и закрепление работают как раньше. ## Не меняется - Своды 1 и 2 (`SummaryTable.jsx`, `Svod2Page.jsx`) и их конфигурации MRT — это М2 и М6, отдельные задачи. - Цена рендера при входящей правке соавтора. Новый массив `data` пересоздаёт все строки и ячейки, мемоизация тут не помогает. Это область AURORA-1838. - Собственный компонент `Cell.jsx` и его стили: их правят AURORA-1890, 1892 и 1894. - Backend, SQL и миграции. ## Capabilities ### New Capabilities - `sheet-table`: таблица листа формы (`RealtimeTable`): - производительность прокрутки; - корректность закреплённых колонок, подсветок и состояния ячеек при мемоизации; - контракт для правок таблицы. ### Modified Capabilities Нет: `openspec/specs/` пуст. ## Impact - Код: `web/src/components/RealtimeTable/`: - `RealtimeTable.jsx`: `memoMode`, ключ сброса, управляемые `columnFilters` и `columnSizingInfo`; - `TableHead/TableHead.jsx`, `TableHead/ColumnResizer.jsx`: начало и конец перетаскивания; - новый модуль ключа сброса в `utils/` и тесты к нему. - Стенд: новый сценарий `perf/e2e/scenarios/pinned-resize.spec.ts`. Добавляется после ночного прогона, отдельным коммитом. - Зависимости не меняются. `material-react-table` 3.2.1 зафиксирован в `web/package-lock.json`. Поведение MRT, на которое опирается дизайн, сверено с этой версией (см. design.md). - Стек PR: ветка `feat/234-aurora-1839-cell-memo` от `feat/226-aurora-1837-cell-styles-css` (AURORA-1837, PR #230). Та, в свою очередь, стоит на AURORA-1836 (PR #222). - Связи: - AURORA-1838: связанная, не блокирующая; - AURORA-1889, 1890, 1891, 1893, 1910: обязаны соблюдать контракт из `specs/sheet-table/spec.md`; - AURORA-1840: независима.