Frontmatter
| id | 9014 |
| title | Refactor Grid Row Selection to use Granular Updates and No-Op Checks |
| state | Closed |
| labels | enhancementaiperformancecore |
| assignees | tobiu |
| createdAt | Feb 6, 2026, 5:26 PM |
| updatedAt | Feb 6, 2026, 5:44 PM |
| githubUrl | https://github.com/neomjs/neo/issues/9014 |
| author | tobiu |
| commentsCount | 2 |
| parentIssue | null |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Feb 6, 2026, 5:44 PM |
Refactor Grid Row Selection to use Granular Updates and No-Op Checks

Input from Gemini 3 Pro:
✦ Clarification on Scope: Granular vs. Column Models
As discussed, this optimization targets Row and Cell based selection models (
RowModel,CellModel,CellRowModel), where interaction typically affects 1-2 items at a time. For these cases, granularrow.update()calls are O(1) and vastly superior to O(N) body updates.Relationship to Column Models: This optimization does not conflict with or degrade
ColumnModel/CellColumnModel.
- Column selection affects all rows (O(N)).
- These models typically trigger a full Body refresh (via
createViewDataorview.update()) to apply column-wide classes efficiently in one pass.- They do not use
updateRowsto iterate every record ID individually.Therefore,
updateRowsremains the specialized tool for granular updates. Making it "smart" (no-op checks) and restoring it as the primary mechanism for Row/Cell selection is a pure win for performance without side effects for column-based logic.

Input from Gemini 3 Pro:
✦ I have implemented the requested optimization for granular grid row updates.
Changes:
Smart
updateRows:
- Refactored
BaseModel.updateRowsto iterate logical items/records.- Added logic to check the current VDOM state (
clsandaria-selected) before applying changes.- The
NeoArrayoperations now use conditional logic based on actual state divergence (shouldSelect !== alreadySelect).- Introduced a
hasChangedflag.row.update()is ONLY called ifhasChangedis true ANDsilentis false.Granular
selectRow/deselectRow:
- Updated
BaseModel.selectRowandBaseModel.deselectRowto remove the costlyview.update()calls.- These methods now delegate entirely to
updateRows(recordId, silent), passing thesilentargument through.- Since
updateRowsnow handles the update granularly (on the specific row component), the full body refresh is no longer needed.This ensures that row selection operations are now O(1) (or O(k) where k is the number of changed rows) rather than O(N) (body size), and redundant updates for unchanged rows are suppressed.
Following the resolution of #9012, we identified that the
RowModelvisual update failure was caused by a logic bug (isSelectedvsisSelectedRow), not an inherent flaw inrow.update(). However, to fix the regression quickly, we fell back to usingview.update()(full Body diff) for row selection.This ticket aims to restore O(1) performance for row selection and further optimize VDOM traffic.
Current State & Inefficiencies
selectRowanddeselectRowcurrently triggerme.view.update(). This forces the VDOM engine to diff every mounted row in the grid body, even though only 1 or 2 rows have changed class. For a grid with 50+ mounted rows, this is wasteful.updateRowstoggles theneo-selectedclass and triggers an update blindly. Even if the class was already there (or already missing),row.update()is called, sending a "no change" delta request to the VDOM worker.The Goal: "Smart" Granular Updates
We want to move back to
row.update()and ensure we only send updates when state actually changes.1. Smart
updateRowsLogicRefactor
Neo.selection.grid.BaseModel.updateRowsto:row.vdom.cls) before toggling.row.update()if a mutation happened.2. Restore Granular
selectRow/deselectRowRefactor
RowModellogic to leverage the fix from #9012 (correct usage ofisSelectedRow) and switch back to granular updates:updateRows(oldId)->rowA.update().updateRows(newId)->rowB.update().3. Disjoint Batching
By issuing two separate
row.update()calls synchronously, the App worker's message batching will automatically combine them into a single payload for the VDOM worker, achieving optimal performance without manual batching logic.Implementation Plan
Modify
updateRows(items, silent):row.vdom.cls.includes(selectedCls)vs target state.cls, setneedsUpdate = true.if (needsUpdate && !silent) row.update().Refactor
selectRow(recordId, silent):deselectRow(id)(triggers granular update for old row).updateRows(recordId, silent)(triggers granular update for new row).view.update()calls.Refactor
deselectRow(recordId, silent):updateRows(recordId, silent).view.update()calls.This will result in a highly responsive grid selection model that scales with complexity (user interaction speed), not dataset size (mounted rows).