useState
useState is a hook that gives a component a cell — a value it can change
— and makes the component follow it. Passed a number, the cell is private to
the component; passed a declaration, the cell is the shared one the
declaration names.
useState(initial: Int): [Int, Setter<Int>]useState(declaration: StateRef<T>): [T, Setter<T>]Reference
Section titled “Reference”useState(initial)
Section titled “useState(initial)”Passing an initial value gives this component its own cell. Every instance of the component has a separate copy, and no declaration is needed.
const PrivateToolbar = ({ x, y }: { x: Int; y: Int }): Component => { const [amount, setAmount] = useState(0);
return ( <layer x={x} y={y} w={BUTTON_W * 4 + 6} h={BUTTON_H}> {AMOUNTS.map((label, i) => ( <layer x={i * (BUTTON_W + 2)} y={0} w={BUTTON_W} h={BUTTON_H} onClick={() => { setAmount(i); }}> {amount === i ? <AmountButtonChosen label={label} /> : <AmountButton label={label} />} </layer> ))} </layer> );};Parameters
Section titled “Parameters”initial: AnIntliteral. The value the first time this instance appears, and ignored afterwards.
Returns
Section titled “Returns”An array of exactly two values: the current value, and the setter that changes it.
Caveats
Section titled “Caveats”- A private cell holds a whole number.
useState('')is refused (TS2E133). A string cell is a module-levelsessionStateorpersistedState. - The initial value is a literal. An identifier in that position means the
other form:
useState(SECONDS)reads as “the cell declared asSECONDS” and is refused (TS2E124) when no such declaration exists. Write the number. - A boolean is written as
0and1, and flipped with1 - value.
const [name, setName] = useState(""); // ints onlyuseState(declaration)
Section titled “useState(declaration)”Passing a sessionState or
persistedState declaration
reads the shared cell instead. Every component reading that declaration
sees the same value.
const SharedToolbar = ({ x, y }: { x: Int; y: Int }): Component => { const [amount, setAmount] = useState(sharedAmount);Parameters
Section titled “Parameters”declaration: A cell declared at the top level of a file.
Returns
Section titled “Returns”The same pair — the current value, and a setter.
set functions, like setAmount(next)
Section titled “set functions, like setAmount(next)”Parameters
Section titled “Parameters”next: The new value, of the type the cell holds.
Returns
Section titled “Returns”Nothing.
Caveats
Section titled “Caveats”- A setter can be called from anywhere — a handler, a function a handler calls, a component that never reads the value. Every reader follows, wherever it is, including in another interface that happens to be open.
- The setter takes the value you want, not a function that computes it. Compute it first, from the value you already read.
- Reading a cell in a handler does not make the component follow it; a read in the markup does. Read a value once at the top of a handler that derives several writes from it.
- A cell that is written but never read is refused (
TS2E228).
Adding a cell to a component
Section titled “Adding a cell to a component”const DisplaySettings = (): Component => { const [roofs, setRoofs] = useState(roofRemoval); const [shiftDrop, setShiftDrop] = useState(shiftClickDrop); <layer x={0} y={0} w="fill" h={ROW_H} onClick={() => { setRoofs(1 - roofs); }}> <CheckboxRow label="Remove roofs" on={roofs} /> </layer>Sharing a cell between components
Section titled “Sharing a cell between components”const openTab = sessionState(0); const [tab, setTab] = useState(openTab); const [tab] = useState(openTab);Setting without reading
Section titled “Setting without reading”const restoreDefaults = (): void => { const [, setRoofs] = useState(roofRemoval); const [, setShiftDrop] = useState(shiftClickDrop);A function that only writes leaves the value out of the pair.
Holding text
Section titled “Holding text”const lastChoice = sessionState(''); const [chosen, setChosen] = useState(lastChoice);A string cell is a module declaration, and it is tested by .length or used
in a template; === between two strings is not supported.
Seeding a cell from the server
Section titled “Seeding a cell from the server” const [stamina, setStamina] = useState(useServerStateOnce(Varbits.STAMINA_ACTIVE));See useServerStateOnce.
Troubleshooting
Section titled “Troubleshooting”Both copies of my component change together
Section titled “Both copies of my component change together”You are reading a shared declaration where you wanted a private cell.
useState(0) is per instance; useState(someDeclaration) is shared on purpose.
The screen does not update when I call the setter
Section titled “The screen does not update when I call the setter”Check that the component displaying the value reads it in its markup. A component that receives the value as a prop from a parent that does not read it never hears about the change — move the read to whichever component should follow it.
I want to store a string
Section titled “I want to store a string”Declare it at the top of the file with sessionState('') or
persistedState('', key) and read that. A private cell cannot hold one.
I want to store several values
Section titled “I want to store several values”Use several cells. There are no objects.