Skip to the content.

Module 2: The write path · Lesson 3 of 7 · 3 min read

Actions: describe what happened, not what to set

Actions are your app's event log. Learn their shape, how to name them, and the request/success/error triple an agent's code must follow.

Events, not setters

The Actions doc sums up the term in one contrast. An imperative update says “set user to X”. An action says “user logged in with credentials X”, and the store decides what that means. Actions are the only way to change state, so every change gets logged, can be traced, and can be reproduced. Replay the sequence and you rebuild the state at any point in time.

Five characteristics make an action an action:

  • It is a plain object: no functions, classes or Promises.
  • It has a type string.
  • It may carry a payload.
  • It follows the Flux Standard Action shape.
  • Nothing changes it after it is created.

Read the templates’ triple

Open templates/chota-react-saga/src/state/todo/todo.type.js. Every async event comes in threes: CREATE_TODO, CREATE_TODO_SUCCESS and CREATE_TODO_ERROR, and the same pattern for READ, UPDATE, TOGGLE and DELETE. The creators in todo.actions.js are one-liners. Each returns { type, payload }, or { type, error } for a failure.

NgRx expresses the same idea with createAction and a [Todo] prefix that names where the action came from, in templates/chota-angular-ngrx/src/app/state/todo/todo.actions.ts: loadTodosRequest, loadTodosSuccess and loadTodosFail. The reducer turns that triple into the loading, data and error states of the UI.

Now compare templates/chota-react-redux/src/state/todo/todo.actions.js. There, createTodo builds its payload by incrementing a module-level nextTodoId. Call it twice with the same text and you get two different actions, so the creator is no longer a pure function of its arguments. That trade-off should be made deliberately, not by accident. The practice question asks for the strict version: same arguments, same action.

What agents get wrong

The doc’s three Common Mistakes are the ones you will catch most often:

  1. Payloads that cannot be serialised. A class instance, a fetch() Promise or a callback ends up in payload, which breaks DevTools, persistence and time-travel debugging. Async work belongs in a thunk or a saga, and the action carries only the resulting data.
  2. Logic in action creators. The creator reads store.getState(), validates its input and returns NO_OP. A creator should only create the action. Synchronous validation belongs in the reducer, and async work in middleware.
  3. Inconsistent naming. ADD_USER, user-delete and updateUser all appear in one codebase. Pick one convention and keep to it. The doc recommends domain/event, and the templates apply their UPPER_SNAKE_CASE constants consistently.

Review checklist for agent output

  • Every type follows the codebase’s single naming convention and is defined once as a constant, not typed inline as a string.
  • Payloads are plain data that JSON.stringify could round-trip without loss.
  • Every async operation has request, success and error actions, and the error action is actually dispatched.
  • No action creator reads the store or calls the network.
  • Any value that varies between calls, such as an id or a timestamp, is generated in exactly one named place and never inside a reducer.

Encode it, then practise it

Agent Skill: state-actions The SKILL.md that encodes Actions 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-actions
Download state-actions SKILL.md

Practise it

Implement Redux-style action creators

Create action creators that return plain action objects for a Redux store.

coding easy state 15 min Solved

Course contents (7 lessons)

Module 1: Where state lives

  1. State: decide where a value lives first done
  2. Store: one source of truth, one way in done

Module 2: The write path

  1. Actions: describe what happened, not what to set done
  2. Reducer: the only place state changes done
  3. Middleware: the pipeline between dispatch and reducer done

Module 3: The read path and async

  1. Selectors: derive, don't store done
  2. Operations: every request is a lifecycle done