Module 1: Where state lives · Lesson 1 of 7 · 3 min read
State: decide where a value lives first
Before an agent writes a reducer, classify every value as local, global, server or URL state. That one decision sizes everything after it.
Four homes for a value
The State doc names three kinds of state, and the state-state skill adds a fourth:
- Local state belongs to one component: an input’s value, or whether a dropdown is open.
- Global state is shared across unrelated components or routes: the signed-in user, the theme, a cart. This is what the store is for.
- Server state is data fetched from an API that has to stay in sync, which brings loading, caching and stale copies with it.
- URL state is shareable and bookmarkable: the page, the filters, the sort order.
The skill’s decision tree is short enough to paste into a prompt. If unrelated components need the value, it is global. Otherwise, if it comes from an API, it is server state. Otherwise, if it should survive a shared link, it goes in the URL. Anything left is local.
What the templates chose
Open templates/chota-react-redux/src/state/ to see those decisions made. rootReducer.js combines three slices: todo, filters and config. The todo slice’s initial state (todo/todo.initial.js) holds todoItems with its lifecycle flags isLoading, isActionLoading, isContentLoading and error. That is server state, stored next to its loading and error flags. The visibility filter gets its own filters slice because the filter buttons and the list both read it. If the filtered view had to survive a refresh or a shared link, the decision tree would send it to the URL instead.
The three mistakes agents make
The doc’s Common Mistakes are what an agent writes when nobody steers it:
- Storing derived data. It keeps
itemCountbesideitemsand updates both. Computeitems.lengthinstead: with a single source of truth, the two can never drift apart. - Mutating state directly. It writes
user.age = 31; setUser(user). React and most state libraries detect a change by comparing references, so this update silently disappears. - Putting everything in global state. It adds
modal1Open,dropdown1ExpandedandinputFocusedto the store. Every subscriber now re-renders, and every component becomes harder to reuse.
The second mistake is why the practice question has you clone data at the store’s edge. When data comes from outside, copy it once on the way in, and never keep a reference that other code can still mutate.
Brief an agent with it
Before you ask for a feature, list its values and where each one lives:
State plan for the product list:
- search box text: local (SearchInput)
- signed-in user: global (store, user slice)
- products: server state, with loading and error tracked
- category filter and page: URL search params
Derive counts and totals in selectors; store none of them.
Then review the diff against the skill’s quality gates:
- No value is stored in two places.
- Derived data is computed, not stored.
- Loading and error states are handled.
- Everything in the store can be serialised.
Encode it, then practise it
SKILL.md that encodes State Basics 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-state
Practise it
Deep clone a value
Copy nested data without sharing references — including the cycle that makes the naive version hang forever.
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