Skip to main content

Views, SubViews, and Children

Why This Matters​

Hosanna uses a declarative view tree. You describe state and return view structs; the framework builds real views, wires parent/child relationships, and mounts native renderers.

Key Concepts​

  • View: A stateful framework object backed by BaseView<T> and a platform renderer.
  • SubViews: Views created by a composite view’s getViews() result. They are declared children and tracked in subViews (flat list) and subViewsById.
  • Children: The on-screen mounted hierarchy. Managed via addChild/insertChild/removeChild and tracked in children and childrenById.
SubViews vs Children

SubViews are the declared structure from getViews(). Children are the mounted, on-screen structure. Most composite views have both.

How Views Are Built​

  1. You update state and return structs from getViews().
  2. Hosanna inflates structs into views via buildView().
  3. Each view onMount() creates a renderer and becomes a child of its parent.
  4. IDs are resolved and stored for fast lookup.

Sequence​

State changes flow through ViewManager, generated view structs, ID-based reconciliation, renderers, and layout invalidationState changes flow through ViewManager, generated view structs, ID-based reconciliation, renderers, and layout invalidation

IDs and Auto-Generation​

  • You can set id on any struct. If omitted, Hosanna auto-generates hierarchical IDs based on position.
  • IDs are stable within a render pass and used for reconciliation and lookups.
Tip: Name Important Views

Assign explicit IDs to views you need to access later (focus, bounds, animations). Let others auto-generate.

How IDs Are Applied​

  • During applyStateChanges, Hosanna calls applyIdsToViewStructs() to ensure each struct has a unique ID.
  • Root auto IDs use the _hsvid_<index> prefix. Descendants append their sibling index to the parent ID, for example _hsvid_0-2. Treat this as an implementation detail, not a test or navigation contract.

Working with SubViews and Children​

  • getSubView<T>(id, deep?) looks up declared subViews (optionally recursive).
  • getChildView<T>(id) returns mounted children by ID.
  • getSubViewRenderer<T>(id) returns a subView’s renderer if mounted.
Tip: Prefer SubView Lookups

Use getSubView for declaration-time relationships and getChildView when you need the mounted instance.

Generics and Safety​

APIs accept generics so you get type-safe results:

const label = this.getSubView<LabelView>('title');
const grid = this.getChildView<GridGroupView>('results');
const renderer = this.getSubViewRenderer<ISGNGroup>('thumbnail');
Caution: Nullability

Lookups can return undefined. Guard before use, especially during initial mount.

Class Relationships​

Declared subview ownership, the mounted child hierarchy, and target-specific renderer trees are related but distinct structuresDeclared subview ownership, the mounted child hierarchy, and target-specific renderer trees are related but distinct structures

Getting Views and Renderers​

  • Views expose renderer?: R. It is present when mounted.
  • The framework's reconciler uses addSubView/insertSubView/removeSubView and addChild/insertChild/removeChild internally. Application views should normally change state and return the desired structure from getViews() instead of mutating these collections.
  • viewOwner links a subView back to its creator.
Where Do Renderers Come From?

onMount() calls createRenderer() and attaches it to the parent’s renderer. On web, renderers are fake SG nodes; on Roku they are SceneGraph nodes.

Focus and Layout Tips​

  • Composite views act as focus groups and can set an initial focus via .isInitialFocus() on a child struct.
  • layoutChildren() positions mounted children using their translation, width, and height.
  • Use getBoundsInParent(view) or getBoundsInApp() when you need math-friendly rectangles.
Tip: Restore Focus Gracefully

Use setFocusedSubview(id) to hint where focus should land when children change.

Troubleshooting​

  • If a lookup fails, verify the ID is actually present on the struct in the latest getViews() output.
  • Ensure the view is mounted before touching its renderer.
  • Prefer explicit IDs on interactive views to avoid auto-ID shifts when order changes.

⚙️ Key APIs​

Key Methods
  • getSubView<T>(id, deep?): Find a declared subView by ID; deep search optional.
  • getChildView<T>(id): Find a mounted child by ID.
  • getSubViewRenderer<T>(id): Get a subView’s renderer.
  • addChild/insertChild/removeChild: Internal mounted-hierarchy operations.
  • addSubView/insertSubView/removeSubView: Internal declaration/reconciliation operations.
Avoid Mutating During Render

Don’t change state inside getViews(). Compute structure only; set state before/after.

Talk to us