Skip to main content

Fragment Performance and Debugging

Fragments are designed for reuse. CollectionView can create, release, and reacquire the same fragment SG tree many times while scrolling. Correct fragment code therefore overwrites all item-specific and status-specific state on every reuse.

Prefer declarative work

Choose the narrowest mechanism that expresses the update:

NeedPrefer
Copy one item value to one SG field${data.path}
Derive one field from item data and literal arguments${fn.name(...)}
Derive primitive fields from data, size, focus, or device inputscomputed.name
Relate child geometryFragment constraints
Measure nodes, coordinate several updates, or manage resourcesFragment callback

Callbacks are an escape hatch, not the default layout system. Imperative node changes are mutable pooled state and must be restored before the fragment is shown for another item.

Make pooled reuse deterministic

  • Put the complete first-render appearance in views.base.
  • In views.normal, restore every field changed by another status.
  • Bind every item-specific field or reset it in onDataChange.
  • Release observers, timers, media, and other resources in onUnmount.
  • Give every addressable descendant a unique child ID.
  • Omit the fragment root id for repeated and prefetched cell styles so the provider can generate unique instance IDs.
  • Do not depend on fragment creation order or generated IDs.

Released fragments retain their SG nodes. A field changed directly by a callback remains changed until a later binding, status overlay, callback, or pristine node-pool reset writes it again.

Constraint layout caching

Set cacheFragmentConstraintLayout: true only when the fragment's constraint writes are stable for the runtime cache inputs.

The runtime automatically splits rules:

  • rules without computed arguments use a geometry-oriented cache key
  • rules with computed arguments use a full key that includes computed values

Raw item data is not independently included in a layout key. Status affects a key only when the computed/constraint dependency graph references focus. Caching is therefore unsafe when callbacks, measured data-bound content, or status overlays change geometry outside those tracked inputs.

cacheFragmentConstraintLayoutKeyMode remains in the public style type, but the current DynamicCell and FragmentView hosts do not consult it when choosing their runtime keys. Do not rely on "full" or "geometry" changing host behavior.

AppConfig invalidation clears the shared constraint-layout cache. A pooled fragment created under an older style or locale revision is discarded when it is released or reacquired; an in-use SG tree is not rebuilt in place.

Content-driven height

contentHeightAnchorViewId runs the fragment constraints against a tall probe height, measures the named child's bottom edge, applies that value to the host, and then runs constraints again.

Use it when one child deterministically represents the content bottom. On a standalone FragmentView, the host height is updated. In CollectionView, the cell's local size can change, but this option does not promise to recalculate the containing row's layout or height.

Diagnose common failures

SymptomCheck
Literal ${data.title} appears$supportsDataMap is true; the binding is an exact views.base field; the child has an ID.
Bound field becomes emptyThe data path exists and resolves to the expected SG field type. Missing paths write undefined.
${fn.*} does nothingRegister the named onSpecificViewDataChange handler before the fragment is first acquired.
Status gets stuck after focus leavesviews.normal does not restore every field changed by focused or another overlay.
First cell ignores views.normalMove the required initial values into views.base; a fresh DynamicCell may already be normal and skip that overlay.
Constraint target cannot be foundIDs are unique, the target exists, and child-to-child references share a parent.
Constraint produces a negative sizeCheck anchor order and margins; dimensions are not implicitly clamped without min or max.
Recycled item shows prior stateA callback or status overlay mutated a field that data binding and normal-state restoration do not reset.
Unknown style renders fallback contentInspect cells.missingCell; without that fallback, missing style acquisition fails.
Shape is missing on RokuEnsure loadShapes() completed and the shape has valid reference dimensions where required.

Inline constraint syntax is validated while AppConfig resolves the style. Missing child references and other explicit-rule problems can surface later when runtime constraint evaluation resolves SG nodes.

Provider diagnostics

ViewFragmentProvider.getFragmentCounts() reports total fragments plus available and created counts by style key. Use it to spot unexpected style churn or a pool that never receives released instances.

ViewFragmentProvider.getDanglingFragmentsForDebug() returns every fragment currently checked out of the available pool. Each row includes the fragment root id, styleKey, parent id and subtype, the renderer _hid when available, and parentIsHolder. Check isDangling first:

  • true with danglingReason: "Fragment view has no SG parent" means the provider considers the fragment in use but its root is detached.
  • true with a fragment-holder reason means acquisition occurred but the root was never attached to its intended row or host.
  • false means the fragment is checked out and attached; it is not evidence of a leak by itself.

Compare these rows with getFragmentCounts() after the owning screen, CollectionView, or FragmentView unmounts. A surviving checked-out row then points to an unmatched host cleanup or release path.

The provider also reports:

  • duplicate child IDs; later matches win lookup
  • callback names that were not registered; regular callbacks become no-ops
  • missing styles and cells.missingCell fallback use
  • unresolved or unavailable compiled shapes

Callback exceptions are not isolated by the provider. Treat callback handlers as application code: validate inputs, keep them synchronous and bounded, and clean up symmetrically.

Current inheritance limitation

Do not rely on fragment $extends to inherit ${data.*}, ${fn.*}, or base-field computed.name mappings. Resolving the base removes those authored strings into its internal map, while resolving the derived fragment rebuilds that map and preserves only constraints and computed declarations.

Until the framework behavior changes, repeat required bindings in the derived fragment or avoid $extends for data-bound fragment trees. Callback groups and ordinary static/status fields still follow their documented merge behavior.

Sample map

The hosanna-ui-samples-public repository contains focused rigs:

CapabilitySource
Standalone updates, callbacks, status, and variable widthsrc/hosanna-ui-examples/rigs/views/FragmentViewRig.ts
Targeted updateData()src/hosanna-ui-examples/rigs/views/BaseViewFragmentRig.ts
Constraint fixturessrc/hosanna-ui-examples/rigs/views/ViewFragmentConstraintFixtures.ts
Inline constraintssrc/hosanna-ui-examples/rigs/collection-view/CollectionViewInlineConstraintsRig.ts
Computed fixturessrc/hosanna-ui-examples/rigs/views/ViewFragmentComputedFixtures.ts
Responsive computed CollectionView cellssrc/hosanna-ui-examples/rigs/collection-view/CollectionViewFragmentComputedVariablesRig.ts
Status combinationssrc/hosanna-ui-examples/rigs/collection-view/CellSelectedStatusRig.ts
Pool cleanup and mutable-state resetsrc/hosanna-ui-examples/rigs/wow-samples/LumenMaskedWorldsCallbacks.ts
Resource and observer cleanupsrc/hosanna-ui-examples/rigs/wow-samples/LumenVideoCellCallbacks.ts
Missing style behaviorsrc/hosanna-ui-examples/rigs/styles/MissingFragmentStyleRig.ts

Use the rigs as implementation evidence, but copy the declarative binding/computed/constraint patterns before adopting callback-driven geometry.

Talk to us