React · Messages and reducers
Conversation State
A chat interface is a state machine for conversations, messages, generation attempts, and transport events. It's more than a text box that keeps appending strings to an array.
Evidence & limits
- What the further reading establishes
- Reducers put related state changes in one place when lots of event handlers touch the same data.
- What this lab runs
- A fixed 18-action trace follows three generation attempts: one completes, one is cancelled and rejects a late delta, and one starts after an edit and regeneration.
- What it does not prove
- This lesson handles state on one device. It doesn't sync edits between people or devices.
Dataset
Conversation Event Log
- Source
- Course-authored synthetic actions
- License
- Not separately licensed
- Size
- 18 reducer actions · 3 generation attempts
Further reading
- Extracting State Logic into a Reducer
Primary · React · Current
Shows how pure reducers and actions keep complicated UI state changes manageable.
- Choosing the State Structure
Guide · React · Current
Covers normalized data, derived state, avoiding contradictions, and using stable IDs.
- Preserving and Resetting State
Guide · React · Current
Explains how component identity and keys decide whether chat state survives a render.
Lesson progressRestoring progress…
- CodeRestoring
- ExperimentRestoring
- CheckRestoring
Summary
Keep state normalized. A conversation keeps messageIds in order, while messagesById stores each message once. Rendering follows the id list. Streaming updates target a message by id instead of assuming the last item in an array is the active one.
Use three different ids. messageId names the long-lived UI record, attemptId names one try at generation, and requestId names one transport run. A regenerated assistant message can stay in the same place in the conversation while getting a new attempt and request.
Don't mutate old state. A delta action returns a new messages collection and a new version of the target message. It keeps every other message's identity unchanged and ignores events aimed at missing or non-streaming targets. That lets React see exactly what changed.
messageIds: ["m-u1", "m-a1"]messagesById · m-u1user · complete · "Explain masking."messagesById · m-a1assistant · streaming · "A causal"m-a1stable UI record→Attempta-17.2one generation try→Requestr-17.2one transport run{ type: "TOKEN_DELTA", messageId: "m-a1", requestId: "r-17.2", delta: " mask" }Guardrequest active ∧ message streaming → applynext !== state · next.m-a1 !== state.m-a1Same objectnext.m-u1 === state.m-u1Available controlscanStop: true · canRegenerate: falseCalculate control state. canStop is true only while the active request is streaming. canRegenerate is true only after an assistant attempt reaches a final state. Calculate both from the normalized records so saved booleans can't get out of sync.
Knowledge check
Implementation
Create the exact message record you can serialize, including the active attempt and transport ids for assistant output.
- Signature
def create_message(options):- Inputs
- options record with id, role, and optional content/status/attemptId/requestId
- Returns
- serializable message dict with the seven canonical fields
- Rule
copy stable ids; default content to empty, status to complete, missing request ids to None, and createdAt to 0- Example
assistant + attempt a1 + request r1 → a streaming record carrying both ids
Reference solution
Approach Create the exact message record you can serialize, including the active attempt and transport ids for assistant output.
def create_message(options): return { "id": options["id"], "role": options["role"], "content": options.get("content", ""), "status": options.get("status", "complete"), "attemptId": options.get("attemptId"), "requestId": options.get("requestId"), "createdAt": 0, }Build message creation and token-delta updates without mutation in Python, then replay the full conversation event log.
Saved results
Results created after your saved code passes its checks
Saved results
Results created after your saved code passes its checks
The replay is course data. The validation result is tied to the code you saved and checked.
Loading…