Overview
useState is the right primitive when local component state is naturally an object — fields, methods, and computed getters together. It returns [wrapper, update]: a read-tracked proxy and the underlying atom’s update handle.
Methods on the initial object are automatically wrapped so calling state.incr() runs the method and notifies subscribers — you do not call update() inside those methods. Direct assignment (state.count = 5) is also reactive.
Getters are version-cached: they recompute when state changes, not on every template read. Prefer this over a pile of separate atoms when the fields always move together.
Compared to atom / setter
|
Member
|
Status
|
Notes
|
|---|---|---|
useState({…})
| object proxy | One reactive bag with methods + getters; component-local. |
atom(value)
| single value | Best for primitives or values shared via props / effects. |
setter
| manual notify |
Plain let + re-render; no property-level tracking.
|
Async anti-pattern
// ✗ throws — async methods not supported
const [bad] = useState({
async fetchData() { /* … */ }
});
// ✓ keep async outside useState
const load = asyncAction(async () => { /* … */ });
useState — object state with methods
Methods defined on the state object are automatically wrapped to trigger a re-render after they run. Getters work as derived values and are re-evaluated on each render.
Direct assign + update()
Assign properties on the proxy for single-field edits. Use update(fn) to batch several mutations into one notify.