Skip to content

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 OnDisable clear every field OnEnable will rebuild?
  • Does OnEnable subscribe even when nothing has changed?
  • Can Subscribe run 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.