Module 1: Where state lives · Lesson 2 of 7 · 3 min read
Store: one source of truth, one way in
What separates a store from a global object, how each template wires one up, and how to stop an agent turning it into a junk drawer.
Four properties, not one object
The Store doc lists four properties that separate a store from any other global object:
- Centralization. There is one well-known location that every component reads from.
- Predictability. State changes only through a defined surface: dispatched actions, store methods, or Pinia store actions.
- Accessibility. Any component at any depth can read or update state without passing it down through props.
- Observability. A subscriber re-renders only when the slice it cares about changes.
Those properties are what make time-travel debugging, action replay, undo/redo and crash reports possible, since every change arrives as a recorded action. Now picture a module-level let user = {} that an agent imports into five files. It has the first property and none of the others.
The same store, five spellings
Every template sets up its store in a few lines, and comparing them shows what is essential:
templates/chota-react-redux/src/state/index.jscallscreateStore(reducer, …)and connects the DevTools extension.templates/chota-react-rtk/src/state/index.jscallsconfigureStore({ reducer: rootReducer }), which sets up DevTools and thunk middleware for you.templates/chota-react-saga/src/state/index.jsusescreateStorewithapplyMiddleware(sagaMiddleware), thensagaMiddleware.run(sagas).templates/chota-angular-ngrx/src/app/app.config.tsregistersprovideStore(reducers)andprovideEffects([TodoEffects])through Angular’s dependency injection.templates/chota-vue-pinia/src/state/todo/index.jshas no root store at all. EachdefineStore('todo', { state, getters, actions })is a store in its own right.
What they share is the essential part. State is organised by domain (todo, filters, config), and there is exactly one way to change it.
What to reject in review
Together, the doc’s Common Mistakes and the skill’s DON’T list make a review checklist:
- Component-local UI state such as
modalOpenorinputValuein the store. Move it back to the component. - A reducer that calls
state.items.push(...)and then returnsstate. It hands back the same reference, so no subscriber sees the change. - A flat store with
userName,cartTotalandproductsLoadingside by side. Group state by domain, so each slice can be split out and combined withcombineReducers. - Values computed from other state but stored anyway, or values that cannot be serialised, such as functions and class instances.
- A second store created “just for this feature”.
Build the smallest one
The practice question strips a store down to its contract. getState() reads the state. dispatch(action) runs the reducer and notifies listeners. subscribe(listener) returns a function that unsubscribes. On creation, the store dispatches @@INIT so reducers can set their initial state. Once you have written those three methods, any store an agent produces is readable, whether it is Redux, NgRx or Pinia. Each one is the same three ideas in different syntax.
A prompt that uses the term precisely:
Add a cart slice to the existing store. Do not create a new store.
Cart state is { items: [], status: 'idle' }; derive the total in a selector.
State changes only through actions; no component mutates cart state.
Encode it, then practise it
SKILL.md that encodes Store for a coding agent. Install it into your agent, or download it for any Agent-Skills-compatible runtime.
npx skills add grvpanchal/elegant --skill state-store
Practise it
Create a Redux-like store
Implement a minimal store with getState, dispatch, and subscribe.
Course contents (7 lessons)
Module 1: Where state lives
Module 2: The write path
- Actions: describe what happened, not what to set done
- Reducer: the only place state changes done
- Middleware: the pipeline between dispatch and reducer done
Module 3: The read path and async