How updates reach the screen
A component redraws when a value it read changes.
What counts as a read
Section titled “What counts as a read”A component follows every value read while producing its markup, including reads inside the functions it calls, however deep:
const poisoned = (): Boolean => useServerState(Varps.POISON) > 0;The build finds the reads in the code, so there is no dependency list to keep in step. The search stops at other components: a child follows its own reads and its parent is not redrawn for them. A read in a handler follows nothing; it is the value when the handler runs.
The reader checks
Section titled “The reader checks”Each component that reads state compares its values once per client cycle, about 20 milliseconds. One case skips the check: a private cell whose every writer is a handler in the same component is redrawn by the setter as the handler returns:
const [n, setN] = useState(0);…<layer x={100} y={0} w={92} h={22} onClick={() => { setN(n + 1); }}>A shared cell gets the same treatment when the build can name every reader and every writer; otherwise all its readers check. Inventories and skills are not checked either: the client reports each change to one, and its readers redraw on the report.
Three tiers
Section titled “Three tiers”The build puts each component on the cheapest tier that is correct.
| tier | what can change | cost while the panel is open |
|---|---|---|
| cache data | nothing | none |
| updated in place | values | a check a cycle; a change re-sets the props that moved |
| rebuilt | what exists | a flip tears the region down and makes it again |
Cache data
Section titled “Cache data”A component that reads nothing is not built. It is stored in the interface’s definition, loaded when the interface opens, and never checked:
const PoisonTitle = (): Component => ( <text w="fill" h={16} text="Poison" font={Fonts.BOLD} color={Colors.AMBER} />);A handler that is one call stays data, because the call and its constant arguments are stored with the element:
onClick={() => { restoreDefaults(); }} />Updated in place
Section titled “Updated in place”A component with a fixed shape is built once. When a value moves, each element is found where it is and only the props that depend on the value are set again:
const PoisonCount = (): Component => { const poison = useServerState(Varps.POISON); return <text w="fill" h={16} text={`${poison} damage`} font={Fonts.PLAIN} color={Colors.WHITE} />;};Ref writes, scroll positions, focus and hover survive. A .map() over a
constant array, a whole collection or a container stays on this tier, because
its rows are the same rows on every build.
Rebuilt
Section titled “Rebuilt”A region is the part of a tree a condition decides. An update cannot set props on an element that does not exist, so when the answer flips, the region is torn down and made again where it sits:
<layer w="fill" h={16}> {poison > 0 && <text w="fill" h={16} text="Cure me" font={Fonts.PLAIN} color={Colors.GREEN} />}</layer>The rest of the component is updated in place around it. Ref writes, focus and
hover inside the region are lost. A loop whose rows are decided while the game
runs, such as one with .filter() in the chain, rebuilds too, and the build
warns.
Where the cost lands
Section titled “Where the cost lands”The tier belongs to a whole component. A read at the root of a panel puts the panel on a checked tier; the same read in the one leaf that uses it costs the leaf, and the rest stays data. A check costs in proportion to how many values a component reads, not how large it is. Every reading component is one more check a cycle, so five components reading one value each cost more than one reading five.
Hidden components are skipped
Section titled “Hidden components are skipped”The client reaches every check by walking the interface tree, and it skips a
hidden component without looking inside. Nothing under a hide is checked,
ticks or fires until it is shown, and then it catches up:
<layer x={2} y={2} w={104} h={{ fill: 4 }} hide={hidden === 1}> <Column title="Hidden while away" /></layer>The client also never walks into a box 0 wide or 0 tall.