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
setStateshallow-merges state. Available- Update scheduling / re-render on
setState. Coming soon- Functional
setStateform (using an updater function). Coming soon- Batching multiple
setStatecalls. Coming soon
Rules of state
- Initialize in
constructorusingthis.state = { ... }. - Do not call
setStatein the constructor. Guards will throw. setState(partial)performs a shallow merge into the current state.- Re-rendering after
setStateis 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
setStateis 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:
setStatewill trigger a diff/patch cycle automatically. - Batching: multiple
setStatecalls 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
setStatein the constructor (guarded). - ❌ Don’t rely on deep merging;
setStateis shallow. - 🔜 Avoid multiple sequential
setStatecalls; batching will arrive soon.
Head to Event Handling (Advanced Patterns) for preventDefault/stopPropagation, keyboard interactions, and composing event callbacks across components.