View Components — Enable, Disable, Rebind¶
A convention for any component that watches a service and draws what it sees: CurrencyBar, InventoryBinder, StatusBuffBar, HealthBarUIConnector, and anything you write in their shape.
Why this page exists¶
Four separate defects, in four carefully written components, all reachable by hiding a HUD panel and showing it again:
| Component | What it got right | What it missed |
|---|---|---|
CurrencyBar | dropped its subscription on disable | re-subscribed only when the service changed, so a hide/show left nothing listening |
InventoryBinder | re-subscribed reliably | no duplicate guard, so three owner swaps left four handlers attached |
StatusBuffBar | subscribed and re-subscribed correctly | primed onto state it had not cleared, inflating every stack count |
CurrencyBar vs CurrencyBarTMP | both probed for a swapped service | only one of the two could be told to stop |
None of these was careless. Each author solved the part of the problem they were thinking about and missed a different part — which is exactly the situation a written convention is for.
The common shape
Every one of these is a mismatch between what OnDisable tears down and what OnEnable rebuilds. If the two are not mirror images, the gap is a bug waiting for someone to hide a panel.
The rules¶
1. OnDisable must leave a state OnEnable can rebuild from scratch¶
Drop the subscription and clear whatever the view derived from it — cached counts, spawned icons, pending timers. Half a teardown is worse than none, because the half that remains looks valid.
StatusBuffBar cleared its pending destroys and left the counts and icons in place.
2. OnEnable must subscribe unconditionally¶
Subscription state and source identity are different things. "The service has not changed, so I do not need to re-subscribe" is false the moment anything drops subscriptions on disable — which OnDisable always does.
CurrencyBar compared the resolved service to the one it held and skipped re-subscribing when they matched. Hiding and showing a bar never changes the service, so it came back with nothing attached, and the per-frame probe applied the same test and so could never repair it.
3. Subscribing twice must be impossible¶
Track what you are attached to and attach at most once. Do not rely on every path detaching first — a public SetOwner or Bind is a path, and it is the one customers call.
InventoryBinder.Subscribe had no guard and Bind never detached, so each rebind added a handler. A subclass that appends rather than rebuilds would show its contents multiplied.
4. Prime from a full read of current state, never by replaying events¶
Replaying "something was added" handlers to catch up assumes you are starting from empty. That is true exactly once, and never again.
StatusBuffBar primed by running its applied-handler over every active status, and that handler increments a stack count.
5. Twin components must offer the same controls¶
If two components differ only in which renderer they drive, a customer choosing between them on that basis must not silently lose a feature.
CurrencyBarTMP had an Auto Rebind toggle for its per-frame probe and CurrencyBar did not.
Checklist¶
Before shipping a view component, answer these:
- Does
OnDisableclear every fieldOnEnablewill rebuild? - Does
OnEnablesubscribe even when nothing has changed? - Can
Subscriberun twice without attaching twice? - Does priming read current state directly, rather than replaying past events?
- Hide and show the panel three times — are counts, icons and totals identical to a fresh open?
- Swap the owner three times — same question.
- If a sibling component exists, does it expose the same options?
The fastest test
Hide and show the panel three times with content on screen. Three of the four defects above show up immediately, and the fourth needs only an owner swap as well.
What this does not cover¶
Polling. Several bars re-resolve their service on a throttled Update probe because a service can appear or swap at runtime. That is a separate concern from subscription lifecycle, and where a bar offers an opt-out (Auto Rebind) it is because a scene that wires its service once at startup does not need the probe at all.
Events raised while disabled. A view that is switched off misses whatever happens meanwhile, and no subscription discipline changes that — which is precisely why rule 1 and rule 4 exist. Rebuild on enable rather than trying to catch up.