Step 1 of 5 · Reading · ~4 min
Read
Production
Capstone: TodoMVC
TodoMVC is the cross-framework benchmark. React, Vue, Svelte, Solid, Angular, Elm and many others each ship an implementation of the same specification, so you can read the same app in any style. It is small enough to hold in your head and big enough to need add, toggle, edit, remove, filter and clear. In this capstone your program is the whole app minus the browser: commands go in, and the HTML the page would show comes out.
Two pure functions
An app of this kind is two functions and one value.
state = reduce(state, command) what happened
html = view(state) what to show
The reducer is the only place state changes, and it is a pure function of the old state and one command. The view is a pure function of the state, so the screen can never disagree with the data (lesson 1's promise). A framework sits between the two: it takes the vnode tree your view builds, diffs it against the last one (lesson 5), and patches the DOM. That is why the same reducer and the same view logic can be dropped under React, your own framework, or a server that sends strings, and why this exercise can be checked by comparing text.
Here is where each earlier lesson shows up. The tree-to-HTML step is lesson 1. Parts that are absent when empty, like the footer, are the conditional children of lesson 2. App, TodoItem and Footer are the components of lesson 3, and data-id is the key of lesson 8. Toggling a checkbox is an event (lesson 7) that dispatches a command, and saving the list to storage would be an effect (lesson 10).
The commands
| Command | Effect |
|---|---|
ADD <text> | trim the text; if anything is left, append a todo with the next id, active. Ids start at 1 and are never reused, even after a removal |
TOGGLE <id> | flip that todo's completed flag |
TOGGLE_ALL | if every todo (and there is at least one) is completed, make all active; otherwise make all completed |
EDIT <id> <text> | replace the text with the trimmed text; if nothing is left, remove the todo |
REMOVE <id> | delete it |
CLEAR_COMPLETED | delete every completed todo |
FILTER all|active|completed | set which todos are visible; anything else is ignored |
DUMP | print the view |
An unknown id changes nothing. The filter hides todos; it never changes the counts.
The view
All markup is on one line. For a state with at least one todo:
<section class="todoapp"><header class="header"><h1>todos</h1></header>
<section class="main"><input id="toggle-all" class="toggle-all" type="checkbox"[ checked]><ul class="todo-list">
<li[ class="completed"] data-id="1"><div class="view"><input class="toggle" type="checkbox"[ checked]><label>TEXT</label><button class="destroy"></button></div></li>
</ul></section>
<footer class="footer"><span class="todo-count"><strong>N</strong> items left</span><ul class="filters">
<li><a[ class="selected"] href="#/">All</a></li><li><a href="#/active">Active</a></li><li><a href="#/completed">Completed</a></li>
</ul>[<button class="clear-completed">Clear completed</button>]</footer>
</section>
(The line breaks and indentation above are only for reading; the output has none.) Bracketed parts appear only under a condition:
- the whole
mainsection and the footer exist only when there is at least one todo, whatever the filter; - the toggle-all box is
checkedwhen every todo is completed; - an
liis listed only if it passes the filter, and hasclass="completed"when completed, placed beforedata-id; its checkbox ischeckedwhen completed; Nis the number of active todos in all, the word isitemwhenNis 1 anditemsotherwise (so0 items left);- the anchor of the current filter has
class="selected"; - the clear button appears when at least one todo is completed.
Text goes through the escape from lesson 1, so a todo called <b>x</b> & "y" shows as text and never as markup.
Common mistakes
- Counting active todos after filtering, so the footer changes when you switch tabs.
- Reusing an id after a removal, which breaks the key contract.
- Treating
TOGGLE_ALLas a plain flip: with a mix, everything becomes completed, and only an all-completed list becomes all active. - Forgetting that the footer vanishes when the last todo is removed, even if a filter is set.
- Escaping twice or not at all, or putting the class after
data-id. - Keeping an
EDITthat trims to nothing instead of removing the todo.
Your exercise
The starter reads the commands, calls your reducer, prints view(state) on DUMP, and gives you esc. You write apply and view. The hidden tests combine several commands, so it is the capability you built across the course, not one rule, that is being checked.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…