Problem
When a :for loop uses key-based diffing (:key), and an item's data changes but its :key stays the same, the reused DOM node's bindings (:text, :if, :class, etc.) are NOT re-evaluated. The old values remain until a full page reload.
Reproduction
<script client lang="ts">
const items = state([
{ id: '1', title: 'Original Title' },
{ id: '2', title: 'Another Item' },
])
function editItem() {
const updated = JSON.parse(JSON.stringify(items.value))
updated[0] = { ...updated[0], title: 'Updated Title' }
items.value = updated
}
</script>
<div :for="item in items" :key="item.id">
<span :text="item.title"></span>
</div>
<button @click="editItem()">Edit</button>Steps:
- Page renders: "Original Title", "Another Item"
- Click "Edit" —
items.valueis reassigned with a new object where itemid: '1'hastitle: 'Updated Title' - Expected: The first item's text updates to "Updated Title"
- Actual: The text stays as "Original Title" until page reload
Root Cause
The key-based diffing in bindFor (added in #1666) correctly matches existing DOM nodes by :key and reuses them. However, when a keyed node is reused, its :text, :if, :class, and other bindings are not re-evaluated with the new item data. The bindings were set up during the initial render and reference the old item object — they don't get rebound to the new item.
Current Workaround
Change the item's id (and thus its :key) when editing, forcing stx to treat it as a new element:
// Instead of keeping the same id:
updated[0] = { ...updated[0], title: 'Updated Title' }
// Change the id so :key differs:
updated[0] = { ...updated[0], id: old.id + '-e' + Date.now(), title: 'Updated Title' }This defeats the purpose of key-based diffing since it forces a destroy + recreate instead of an in-place update.
Expected Behavior
When :for reuses a DOM node by matching :key:
- The item variable in the loop scope should be updated to reference the new data
- All bindings (
:text,:if,:class,:show,@clickhandlers) should re-evaluate with the new item - The DOM node itself should be preserved (no destroy/recreate) — only the content updates
This is how Vue's v-for + :key works: keyed nodes are reused but their bindings are patched with new data.
Impact
Any editable list where items are modified in-place (e.g. calendar events, todo items, table rows) requires the :key workaround. Without it, edits appear to not save until page reload.
Environment
- stx version: 0.2.10 (local linked)
- Discovered in a calendar app where editing a workout card's title via a modal didn't reflect in the grid until reload
🤖 Generated with Claude Code