@state
Marks a field as reactive. Read it while rendering and the component renders again whenever it is assigned.
The situation it is for
A search box. The typed text has to be remembered between renders, and changing it has to redraw
what is on screen — that is what a @state field is:
import type { EventOn } from "@ramonda/core";
class Search extends Component {
@state query = "";
onType(e: EventOn<HTMLInputElement>) {
this.query = e.currentTarget.value;
}
render() {
return (
<label>
Search
<input value={this.query} oninput={this.onType} />
</label>
);
}
}
Two things happen because of the one decorator. this.query = … in the handler is a change, so
the component renders again. And render() reading this.query is what ties it to the field —
nothing declares that, and a render that stopped reading it would stop being woken by it.
There is no setter and no separate place for state to live. It is a field: read it with
this.query, change it with this.query = "ada".
See State for the reactivity model this is the front door to.
Assignment is what fires
A signal fires when it is assigned, not when the value it holds changes inside. So this does nothing:
class Recent extends Component {
@state queries: string[] = [];
remember(query: string) {
this.queries.push(query);
}
render() {
return <p>{this.queries.length} searched</p>;
}
}
and this does:
class Recent extends Component {
@state queries: string[] = [];
remember(query: string) {
this.queries = [...this.queries, query];
}
render() {
return <p>{this.queries.length} searched</p>;
}
}
Mutating in place is reported both ways — as RMD005 when it runs,
and by ramonda-check as state-mutated-in-place before it does.
What it refuses
Anything but a field. A getter that derives is @compute; a
method is not state.
A write during a render. render() reads state; it does not write it. A write there schedules
another render from inside a render, and is reported as
RMD001 — with
state-written-while-rendering finding it in the source,
including through a helper the render calls three files away.
What it costs
One signal per field, and a comparison on assignment: writing the value it already holds re-renders nothing.
It is part of the hydration blob. A @state field is serialised so the browser resumes with the
value the server had — which is why @persist beside it adds
nothing and is reported as RMD046. A value that cannot be
serialised is reported as unserializable-state.