Lesson 5
Replace, movable, and reusable groups
Distinguish control-flow replacement, state-preserving movement, and node reuse in the runtime protocol.
Status: The three roles—replace a control-flow region, move keyed content, and reuse content or nodes—are Durable protocol distinctions. Their method names and lifecycle details are Version-specific at the pinned revision.
Outcome
When you see a group call, ask what kind of continuity it promises:
| Group | Continuity question |
|---|---|
| Replace | Can this region only be inserted, removed, or replaced? |
| Movable | Can this keyed content move to another position while keeping identity? |
| Reusable | Can existing content or nodes be prepared for a different data key? |
They solve different reconciliation problems.
Replace: control flow, not movement
An if branch has a fixed logical location, but its selected content can change. startReplaceGroup(key) and endReplaceGroup() mark it as non-movable replacement work.
The current Composer.kt retains startReplaceableGroup for older compiler output while newer compilers use startReplaceGroup.
Movable: identity follows a data key
A movable group adds a data key to a compiler source key:
@Composable
fun Item(id: String) {
key(id) {
remember { expensiveObject(id) }
Content(id)
}
}
The compiler key describes source location. The data key describes which item occupies it. startMovableGroup(key, dataKey) lets tested keyed content move with its key.
id is not globally unique; it must distinguish relevant siblings in the local parent context. Duplicate local keys leave matching ambiguous.
flowchart LR
SourceKey[Compiler source key] --> Pair[Group identity pair]
DataKey[Item data key] --> Pair
Pair --> Same[Same item at new position]
Pair --> Move[Move content and identity]
ReplaceKey[Branch key only] --> Replace[Replace region]
ReuseKey[Reuse data key] --> Reuse[Prepare existing content]The visual shows roles, not a slot-table address format. Storage is Chapter 3. MovableContentTests.kt includes movableContentPreservesNodes and remembered-value move tests. They support a bounded claim about the tested path, not every structural edit.
Reusable: prepare for a different occupant
ReusableContent(key) { ... } calls startReusableGroup with a reuse key. When that key changes, the runtime can enter a reusing state: cache values may be invalid and changed checks can report differences until a reusable node is reached.
Reusable content therefore refreshes a reusable shape; it does not promise that every remembered value follows a new item. CompositionReusingTests.kt tests reuse, deactivation, and release callbacks.
Experiment: reorder, then change the reuse key
Build a test composition with keyed items and one reusable region. Compose A, B, then recompose B, A, recording remembered-token and node identity. Change a ReusableContent key from A to C and record callbacks and recalculation.
Control: remove key(id). Expected: the keyed path can match by data key; the unkeyed path has only position. Limit: callback names and node operations are tied to the cited tests.
Misconceptions
- “Movable and reusable mean the same thing.” Movable preserves identity across position; reusable refreshes an occupant.
- “Replace groups can move when keys match.” They are non-moving control-flow boundaries.
- “A data key is enough.” Identity also uses source context and local position.
- “Reusable content preserves every
remembervalue.” Reuse includes invalidation and refresh.
Check yourself
A list item moves from index 0 to index 1 but keeps the same stable ID. Which group role expresses that continuity? A view-holder-like node is then reused for a different ID. Which role expresses that change, and why is it not the same promise?
Evidence notes
| Claim | Direct evidence | Status |
|---|---|---|
| Replace groups are non-moving insert/remove/replace regions | Composer.kt, startReplaceGroup | Durable
Version-specific |
| Movable groups pair a compiler key with a data key so keyed content can move | Composer.kt, startMovableGroup, and MovableContentTests.kt | Durable
Version-specific |
| Reusable groups change composer behavior when their reuse key changes | Composer.kt, startReusableGroup, and CompositionReusingTests.kt | Version-specific |
| Current source retains an older replaceable method for generated-code compatibility | Composer.kt and Compose Compiler 1.5.11 release notes | Version-specific |
Freshness
Refresh this lesson when the Composer group contracts, ReusableContent, movable-content tests, or compiler compatibility fallbacks change. Re-run the reorder/reuse experiment against the new pinned runtime before making a stronger lifecycle claim.
Finished this lesson?
Your progress stays only in this browser.