State Management
State holds data that changes over time inside a class component.
Initialize it in the constructor and update it with setState
after mount.
Status
setState
shallow-merges state. Available- Update scheduling / re-render on
setState
. Coming soon- Functional
setState
form (using an updater function). Coming soon- Batching multiple
setState
calls. Coming soon
Rules of state
- Initialize in
constructor
usingthis.state = { ... }
. - Do not call
setState
in the constructor. Guards will throw. setState(partial)
performs a shallow merge into the current state.- Re-rendering after
setState
is coming soon (merging works today; scheduling/patching will land shortly).
Initialize state
import { AtomComponent } from '@atomdev/core';
interface State {
count: number;
loading: boolean;
}
export class Counter extends AtomComponent<{}, State> {
constructor(props: {}) {
super(props);
this.state = { count: 0, loading: false };
}
render() {
return <p>Count: {this.state.count}</p>;
}
}
Update state after mount
Use setState
(post-mount) to merge partial updates.
export class Counter extends AtomComponent<{}, { count: number }> {
constructor(props: {}) {
super(props);
this.state = { count: 0 };
}
increment = () => {
this.setState({ count: this.state.count + 1 }); // merges; re-render scheduling coming soon
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.increment}>Add</button>
</div>
);
}
}
Why not in the constructor? The base class enforces that
setState
is only used after the component has mounted.
Shallow merge semantics
setState
merges one level deep:
interface State {
user: { name: string; theme: { mode: 'light' | 'dark' } };
}
export class Profile extends AtomComponent<{}, State> {
constructor(props: {}) {
super(props);
this.state = { user: { name: 'Ava', theme: { mode: 'light' } } };
}
// This replaces `user.theme` entirely unless you spread the previous value
setDarkMode = () => {
this.setState({
user: { ...this.state.user, theme: { mode: 'dark' } },
});
};
render() {
return (
<div>
<p>{this.state.user.name}</p>
<button onClick={this.setDarkMode}>Dark Mode</button>
</div>
);
}
}
If you need deep updates, spread nested objects yourself (or use your own helper) before passing to setState
.
Scheduling, batching, and functional updates (coming soon)
Planned enhancements:
- Re-render scheduling:
setState
will trigger a diff/patch cycle automatically. - Batching: multiple
setState
calls in the same tick will be coalesced. - Functional form:
setState(updater)
whereupdater(prevState, props)
returns the partial state, avoiding stale reads during batching.
// Example API (coming soon)
this.setState((prev) => ({ count: prev.count + 1 }));
Until these land, prefer a single setState
per user action and compute new state from the current instance state.
Deriving state from props
When new props arrive, you may want to adjust state.
- For simple cases, compute values inside
render()
from props—no extra state required. - For synchronized state (e.g., caching a prop value), a hook like
beforePropsUpdate(nextProps)
is planned. Coming soon
Side effects with state
Start effects that depend on the DOM in afterMount
and clean them in beforeUnmount
.
export class Ticker extends AtomComponent<{}, { t: number }> {
private id?: number;
constructor(props: {}) {
super(props);
this.state = { t: 0 };
}
afterMount() {
this.id = window.setInterval(() => {
this.setState({ t: this.state.t + 1 }); // re-render scheduling coming soon
}, 1000);
}
beforeUnmount() {
if (this.id) window.clearInterval(this.id);
}
render() {
return <span>{this.state.t}s</span>;
}
}
Patterns & pitfalls
- ✅ Initialize once in the constructor with
this.state = {...}
. - ✅ Treat state as immutable; construct new objects when updating.
- ✅ Prefer computing derived values in
render()
when feasible. - ❌ Don’t call
setState
in the constructor (guarded). - ❌ Don’t rely on deep merging;
setState
is shallow. - 🔜 Avoid multiple sequential
setState
calls; batching will arrive soon.
Head to Event Handling (Advanced Patterns) for preventDefault/stopPropagation, keyboard interactions, and composing event callbacks across components.