When a user does something — connects a service, completes a task, sends a message — the screen should reflect it instantly, with no refresh button, no reopening the screen, and no hand-written copy of the new state. This page shows the one loop that makes that happen.
The product moment
A user clicks Connect on a service tile. The credential is saved. The tile should flip to Connected the moment the save lands — on its own.The wrong way to get there is to fake it: flip the tile in component state because you “know” the connect succeeded, or re-pull the whole feed every time the screen mounts. Both drift from the real data, and both are extra code you later have to delete. The right way is a single reactive loop where the rendered screen is always a direct projection of the saved data.The loop
A surface asks a target to do work with sendRequestToBitfieldTarget(…).
The target writes the package records the prepared value is built from.
A change-driven rule re-prepares the named value from the new records.
useBitfieldData(…) receives the new value and React re-renders.
The read side is already live
useBitfieldData(...) is not a one-time fetch. It is a live subscription to a named value. When that value changes, every component reading it re-renders with the new value.The write side changes the records
A user action asks a target to do work. That target writes the package records the named value is prepared from.The missing link: one reactive rule
Between “records changed” and “named value changed” sits the piece people forget. The prepared value does not rebuild itself just because some record changed somewhere. A package declares a reactive rule that watches the records the value depends on and re-prepares the value when any of them change.State the rule with three facts:| Part of the rule | What you provide |
|---|---|
| What to watch | The records (or the fields) the prepared value is built from. |
| When to fire | On a change to any of those watched records. |
| What to do | Re-prepare the named value the surface reads. |
Make the rule and the projection match
The most common reason a correct-looking loop still shows stale state: the reactive rule watches one set of records, but the prepared value is built from a different set. The write updates records the rule does not watch, or the rule rebuilds from records the write does not touch. Keep one answer to “which records carry this truth,” and point both the rule and the projection at it.Anti-patterns
These three shortcuts all look like they work and all fight the loop. Delete them.Optimistic local copy. The component keeps its ownconnectedIds set and flips a row to connected because the request resolved. Now there are two truths — the local set and the prepared value — and they drift the first time a write partly fails or another surface changes the same data. Let the write change the records and let the rule rebuild the value. The render is the truth.Re-pull on mount. The surface rebuilds the whole feed in a mount effect “so it is fresh when the screen opens.” This hides a missing reactive rule: it makes opening the screen the refresh, instead of the data change being the refresh. It also does nothing while the screen is already open. Remove the mount effect and add the rule.Manual rebuild after every write. Each action calls the rebuild itself right after writing. This works until one new caller forgets, and it couples every writer to the projection. One reactive rule replaces every one of these calls and can never be forgotten.What this prevents
| Bug | Cause it removes |
|---|---|
| Tile flips, then snaps back. | Optimistic local copy drifting from the rebuilt value. |
| Screen only correct after reopening. | A missing reactive rule masked by a mount-time re-pull. |
| One screen updates, another does not. | A writer that forgot its manual rebuild call. |
| Action succeeds but nothing visibly changes. | The write changed records the projection does not read. |
Verify
| Check | Pass condition |
|---|---|
| Live read | The component reads the named value with useBitfieldData(...) and renders loading, error, empty, and data states. |
| Write target | The user action calls a target with sendRequestToBitfieldTarget(...); it changes the records the value is built from. |
| Reactive rule | A package-declared rule rebuilds the named value when those records change. |
| No second truth | The component holds no optimistic copy, no mount-time re-pull, and no manual rebuild call. |
| Instant update | Doing the action updates the open screen with no refresh and no reopen. |
What should happen
Next
Read Read data in React for the read hook contract and selectors.Read Send a request for payloads, replies, and request errors on the write side.Read React surface for package data for a full read-plus-request surface to attach this loop to.Read Package to screen for the full path from package data to a rendered surface.Read Runtime Kit API for the exactuseBitfieldData(...) and sendRequestToBitfieldTarget(...) contracts.