feat(nanoviews): hand the tracking key to the row - #217
Merged
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #217 +/- ##
=======================================
Coverage 85.38% 85.38%
=======================================
Files 140 140
Lines 3154 3155 +1
Branches 594 594
=======================================
+ Hits 2693 2694 +1
Misses 332 332
Partials 129 129 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
A row is identified by the key its tracker named it by, and that key is the one thing about a row that cannot change: a different key is a different row. Until now the loop kept it to itself, so a component that needed the key had to read it back out of the row reactively - paying a child signal, an effect and a few links for a value that was already known and would never move - or capture it from the item it was built with, which is wrong the moment the row is reused. The render function now receives it third, as a plain value: `($row, $index, key) => …`. `as_` carries it through the transform, and the static array arm hands over the index, which is the key a list without a tracker is reconciled by. `trackById` and `trackBy` became generic over the value they return, so the key arrives typed instead of `unknown`. Reading the key rather than a signal over it takes 19% off the script time of `01_run1k` and 366 KB off the memory of a thousand rows; it costs 11 bytes gzipped.
dangreen
force-pushed
the
feat/nanoviews-row-key
branch
from
August 25, 2026 16:03
d34b7af to
4e1ca5f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A row is the row its tracker named. The key is the one thing about it that cannot change — a different key is a different row — and until now the loop kept it to itself:
$idis a signal over a value that was known before the row existed and will never move. It costs a child signal, an effect and links, on every row, to read something the loop already had. The alternative — closing over the item the row was built from — is wrong the moment the row is reused for another item.So the key comes third, as a plain value:
Three details:
as_carries it through.as_(record, ($row, $index, key) => …)works the same.each_straight toArray#map, so a third parameter received the array — a trap for anyone who ever wrote one.trackByIdandtrackBybecame generic over what they return. They were typed=> unknown, which would have made the keyunknownand the whole thing useless.Without a tracker the key equals the index and the two never disagree: the lookup is keyed by position then, so the row at position
iis always the one whose key isi.Measured
The row's key is only worth something to a component that takes it, so this was measured with the benchmark app rewritten to use it (a separate PR). 30 iterations per arm, two rounds with the arms alternating:
22_run-memorygoes from 3.449 MB to 3.083 — 366 KB less for a thousand rows, with both rounds agreeing to the third decimal.What leaves each row is the whole reactive apparatus that existed only to deliver the id: the proxy trap, a child computed, an effect and three links.
Cost
+11 B gzip (7558 → 7569). No pin moves. If #216 lands first its
Average usagepin already covers the pair; whichever goes second wants its pins re-checked after the rebase.Four tests: the key in the reactive arm and that it stays with a row across a reorder, the index as the key without a tracker, the same for a static array, and the key through
as_. Lint,tsc --noEmitand 131 tests are green.