Skip to main content
MediumFull StackBuild

Todo App with API Persistence

Build the UI for a todo app whose data lives in a backend. You're given three async props that stand in for the API: `loadTodos()` returns the current list, `addTodo(text)` creates a todo on the server and returns it, `...

What you will practice

TypeScriptReactReact PatternsAsync PatternsAPI IntegrationPersistence

Requirements

  • Keep `App` as the default exported React component with the provided `loadTodos`, `addTodo`, and `deleteTodo` props.
  • Call `loadTodos()` exactly once on mount and render each returned todo inside a list with `data-testid="todo-list"`.
  • Render each todo as an element with `data-testid="todo-item"` showing the todo's `text`.
  • Render an input with `data-testid="todo-input"` and an `data-testid="add-button"` button for creating new todos.
  • When the add button is clicked with non-empty input, call `addTodo(text)` once with the trimmed text and append the resolved todo to the list.
  • Each todo item must contain a `data-testid="delete-button"` button. Clicking it calls `deleteTodo(todo.id)` and removes that todo from the list once the call resolves.

Starter files

App.tsxEditable starter

What the judge checks

  • Runs in the react environment with the react-testing-library runner.
  • Uses a 8000ms judge budget.
  • Requires test IDs: todo-list, todo-input, add-button.
  • Checks repeated UI regions: todo-item.
  • Provides APIs: loadTodos, addTodo, deleteTodo.
  • Behavior rules include: Load Todos Renders Existing Items, Add Todo Calls Api And Renders Result, Delete Todo Calls Api And Removes Item.

Constraints

  • `loadTodos` must be called at most once per mount.
  • `addTodo` must not be called with an empty string.
  • The UI must not append a new todo before `addTodo` resolves — wait for the server to confirm the id.

Example behavior

Input
loadTodos() resolves with [{ id: 1, text: 'Buy milk' }, { id: 2, text: 'Walk dog' }]
Output
Two todo-item elements are rendered: 'Buy milk' and 'Walk dog'
Input
User types 'Pay rent' and clicks add. addTodo('Pay rent') resolves with { id: 3, text: 'Pay rent' }
Output
A third todo-item with 'Pay rent' appears in the list

Follow-up

How would you handle a failed `addTodo` — show an inline error, retry, or roll back optimistically?