Compile Ready
Module 14 · Machine Coding

Build: Todo App

Intermediate13m read40m practice53m total
Machine CodingStateRenderingEvents

Introduction

A Todo app is the smallest UI challenge that still tests real product architecture: state shape, immutable updates, rendering, event delegation, filters, and persistence boundaries.

A strong answer does not scatter DOM mutations across handlers. It centralizes state transitions in a reducer-like function and uses one render path so every action produces a predictable UI.

Why This Matters

Todo apps are deceptively useful in interviews because they reveal how you structure frontend code under time pressure. Interviewers watch for stable ids, event delegation, empty states, XSS-safe rendering, and whether state is the source of truth.

Theory

State model

Each todo should have a stable id, a text, and a completed flag. The app state also tracks the active filter and the next id. Avoid using array indexes as ids because removing or reordering items breaks event targeting.

Render strategy

For a framework-free implementation, generate HTML from state in one render function and attach one delegated click listener to the root. Event delegation is simpler than registering a listener on every item after every render.

Reducer mindset

Actions such as add, toggle, remove, clear completed, and set filter are state transitions. Keeping them in dispatch(action) makes the app easy to test without the DOM. Rendering becomes a side effect after state changes.

Visual Diagrams

Todo app architecture
user event
   |
   v
dispatch action
   |
   v
update state
   |
   v
render from state
   |
   v
DOM reflects current state

State is the source of truth; the DOM is a projection of state.

Code Examples

Complete Todo app with delegated events

The render function deliberately builds strings with concatenation. In production, a framework or DOM node construction can replace this rendering layer while preserving the state model.

Loading…

Playground

Loading editor…
Console

Press Run to execute the code and see output here.

Output Prediction

Predict the output #1

javascript
1function reduceTodos(state, action) {
2 if (action.type === 'add') {
3 return {
4 nextId: state.nextId + 1,
5 items: state.items.concat({ id: state.nextId, text: action.text, completed: false })
6 };
7 }
8
9 if (action.type === 'toggle') {
10 return {
11 nextId: state.nextId,
12 items: state.items.map(function (item) {
13 if (item.id !== action.id) {
14 return item;
15 }
16
17 return { id: item.id, text: item.text, completed: !item.completed };
18 })
19 };
20 }
21
22 return state;
23}
24
25var state = { nextId: 1, items: [] };
26state = reduceTodos(state, { type: 'add', text: 'Ship content' });
27state = reduceTodos(state, { type: 'toggle', id: 1 });
28console.log(state.items[0].text + ':' + state.items[0].completed);

Coding Exercises

Implement a framework-free Todo app

Medium

Build createTodoApp(root).

Requirements:

  • Add todos from a form.
  • Toggle completion by stable id.
  • Remove todos.
  • Filter all, active, and completed.
  • Clear completed todos.
  • Render empty and remaining-count states.
  • Escape user text before inserting HTML.
  • Use event delegation and return { dispatch, getState }.

Constraints:

  • Do not use a framework.
  • Do not use array indexes as ids.
  • Keep state as the source of truth.

Interview Questions

1Why should you avoid array indexes as todo ids?

Indexes change when items are removed, filtered, or reordered. If the UI stores an index in the DOM, a later click may target the wrong item. Stable ids keep event handling correct across list changes.

Asked at:MicrosoftAtlassian

Follow-ups

  • How would you persist todos?
  • How would you avoid rerendering the whole list?
2Why use event delegation?

One root listener can handle events for current and future child elements. This avoids reattaching per-item listeners after every render and makes cleanup simpler.

Quiz

1. What is the main reason to escape todo text before writing `innerHTML`?

Summary

  • Keep Todo state as the source of truth.
  • Use stable ids, not array indexes.
  • Centralize transitions in a dispatch function and render from state.
  • Escape user text before string-based rendering.

Cheat Sheet

Todo app checklist

  • State: { items, filter, nextId }.
  • Item: { id, text, completed }.
  • Actions: add, toggle, remove, clear completed, set filter.
  • Event delegation on root.
  • Stable ids in data-id.
  • Escape text if using innerHTML.
  • Render empty state and remaining count.