How state and events work in picoapp
masterpicoapp uses a shared state model. You can define initial state when creating the app instance, and components interact with this state via the ctx (context) object.
Initializing State
Pass a state object as the second argument to the picoapp constructor:
const app = picoapp({ button }, { count: 0 });Consuming State
Use ctx.getState() inside a component to access the current state:
export default component((node, ctx) => {
const count = ctx.getState().count;
});Updating State via Events
To update state, use ctx.emit(eventName, payload).
- If the payload is an object, it is merged into the global state.
- If the payload is a function, it receives the current state and should return the new state object.
// Emitting an object (merges into state)
ctx.emit("increment", { count: ctx.getState().count + 1 });
// Emitting a function (uses previous state)
ctx.emit("increment", state => {
return { count: state.count + 1 };
});Listening to Events
Use ctx.on(eventName, callback) to react to state changes. The callback receives the entire state object.
Subscription patterns:
- Multi-subscribe:
ctx.on(["prop1", "prop2"], state => {}) - Wildcard:
ctx.on("*", state => {})(fires on all updates) - Property keyed:
ctx.on("someProp", ({ someProp }) => {})(fires whensomePropupdates)
Updating State without Events
Use ctx.hydrate(newState) to update the state directly without triggering an event.
ctx.hydrate({ count: 12 });export default component((node, ctx) => {
ctx.on("increment", state => {
node.innerHTML = `I've been clicked ${state.count} times`;
});
node.onclick = () => {
ctx.emit("increment", { count: ctx.getState().count + 1 });
};
});