FrontendPro

145Build a Trello-like Kanban app supporting nested cards, drag-and-drop, undo/redo, and persistence

hardAbout 90 minutesCodingstate-managementundo-redopersistencetrees

Problem

Build the state layer of a Trello-like board. (Drag and drop events are covered in #80; here every drag ends in a moveCard command.)

createBoardStore({ storage, key = "board", initial = { columns: [], cards: {} }, historyLimit = 100 }) returns { getState, dispatch, undo, redo, canUndo, canRedo, subscribe }.

State: { columns: [{ id, title, cardIds }], cards: { [id]: { id, title, parentId, childIds } } }. A card either sits in a column (parentId: null, listed in the column's cardIds) or is nested under another card (parentId set, listed in that card's childIds).

Commands for dispatch(command):

  • { type: "addColumn", id, title }
  • { type: "addCard", id, title, columnId } or { type: "addCard", id, title, parentId }
  • { type: "editCard", id, title }
  • { type: "moveCard", id, columnId?, parentId?, index }: move to a column or under a card, at index (counted after removing the card from its old place). Moving a card moves its nested cards with it. Throw an Error when moving a card under itself or its own descendant.
  • { type: "deleteCard", id }: delete the card and all its nested cards.

Requirements:

  • State is immutable: every command produces a new state; untouched columns and cards keep their identity.
  • undo()/redo() step through history; a new command after undo clears the redo history; at most historyLimit steps are kept.
  • After every change (command, undo, redo) save { version: 1, state } as JSON in storage (getItem/setItem). On creation, load it; if it is missing, corrupt or a different version, use initial.
  • subscribe(listener) calls listener(state) after every change and returns an unsubscribe function.

Try it yourself

Write your solution, run it, then submit to check it against the tests.

solution.jsJavaScript

Press Run or ⌘ Enter to execute your code.

Step-by-step

reduce(state, command) returns a new state and never mutates the old one. Everything else (history, persistence, subscriptions) wraps it. Pure reducers make undo trivial: old states are still intact.

Unlock the full solution

What's inside

  • Step-by-step
  • Approach
  • Solution
  • Complexity
  • Edge cases
  • Follow-up questions
  • Tests

Includes 2 code listings, 5 edge cases, 4 follow-up questions.

One-time payment by UPI, card or net banking. Launch price.

All 150 answersOne payment, yours forever