Checkpoint: Notifications

The Course
•
4 min read

Transfer the reducer pattern

A notification model has records with id and read fields. Write reduceInbox(state, action) supporting markRead by id and clearRead. markRead returns a new list with the chosen record read; a missing id changes no record values. clearRead removes read records. Unknown actions throw an error. The input state is an array of already-valid records.

Write checks for unchanged input, repeated markRead, a missing id, empty input, and clearing an all-read inbox. Keep printing, storage, and network requests outside the reducer. Decide which unchanged records may remain shared.

A reducer is not tied to a particular framework. The useful habit is to make state transitions visible and testable. Spend this block implementing the model, then explain how a controller could translate a button click into an action without putting DOM code into the reducer.

Predict and trace

Write the output before running. Follow the values through each call; if your prediction differs, identify the first step that surprised you.

Predict, then run JavaScript
const before = [{ id: "a", read: false }];
const after = before.map(item => ({ ...item, read: true }));

console.log(before[0].read);
console.log(after[0].read);
Check your prediction
false
true

Copying the changed record preserves the earlier snapshot.

Acceptance cases before you code

Write a small test plan before implementing reducer preservation. These cases extend the brief; they are not a second worked solution. Predict the expected behavior independently, then test through the public function. A good ordinary example and a good boundary example should catch different possible mistakes.

Case to tryWhat your result should demonstrate
Mark a matching record readNew state has the updated flag and old state does not
Clear read itemsRetain unread records and unrelated state
Unknown idApply the documented no-match policy without editing another record

After the core task passes, deliberately break one rule and identify the case that notices it. Restore the implementation and explain the failure in terms of the contract, not the number of assertions. If a case requires a concept you have not recalled, revisit the relevant lesson before opening the solution.

Build it yourself

Exercise

Checkpoint: Notifications: practise

Implement reduceInbox. Mark a read, clear read items, and print the original first flag and remaining id.

Your working space JavaScript
const inbox = [
  { id: "a", read: false },
  { id: "b", read: false }
];
Hint 1: the idea

One action changes a record; the other changes membership.

Hint 2: a first step

Use map for markRead and filter for clearRead; reject the default case.

Show one solution
function reduceInbox(state, action) {
  switch (action.type) {
    case "markRead":
      return state.map(item =>
        item.id === action.id ? { ...item, read: true } : item
      );
    case "clearRead":
      return state.filter(item => !item.read);
    default:
      throw new Error("Unknown action");
  }
}

const inbox = [
  { id: "a", read: false },
  { id: "b", read: false }
];
const marked = reduceInbox(inbox, { type: "markRead", id: "a" });
const remaining = reduceInbox(marked, { type: "clearRead" });

if (inbox[0].read !== false) {
  throw new Error("input changed");
}

console.log(inbox[0].read);
console.log(remaining[0].id);
false
b

Write the remaining cases from the brief. If you persist the inbox, design a decoder for restored records instead of trusting parsed JSON.

Test a changed requirement

Write the remaining cases from the brief. If you persist the inbox, design a decoder for restored records instead of trusting parsed JSON.

Explain before moving on

Which action is safe to repeat? What tests would expose mutation and an accidental removal of unread records?

Was this article helpful?

Your response is saved on this device.