#State#Components

To-Do With Filter Tabs

A task list with In progress, Completed and Deleted tabs — where deletion cannot mean removal.

By Pratik RaiEasy

A task list with three filter tabs — In progress, Completed and Deleted — and Enter to add.

The third tab is the entire exercise, and it is the requirement people skim past. A to-do list where deleting means filter(t => t.id !== id) cannot show a Deleted tab at all, so the data model has to be right before any of the UI works.

One status field rather than two booleans is the call to make. With isCompleted and isDeleted you can represent a task that is both, which means every filter has to decide what that means — and different parts of the code will decide differently. A single status of in-progress, completed or deleted makes the invalid combination impossible to write down.

Everything else follows from that. The visible list is derived by filtering at render time, the per-tab counts are derived the same way, and every action is one map that replaces a task with a copy carrying a new status. No action needs to know about any other tab, and nothing is ever spliced out of the array.

It is the easiest of these problems and still a real test, because the modelling mistake is available and tempting. A reasonable follow-up is persistence, and the shape already suits it: one array of self-describing tasks serialises directly.

Goal: Model three task states cleanly and derive each tab from one array.

Source

Frequently asked questions

Why can't delete just remove the task from the array?
Because the Deleted tab has to list it. Deletion here is a state change, so nothing is ever spliced out — which is what makes the data model the actual exercise.
Why use one status field instead of two booleans?
With `isCompleted` and `isDeleted` a task can be both, and every filter has to decide what that means — inconsistently. A single status of `in-progress`, `completed` or `deleted` makes the invalid combination impossible to represent.
Should you store a separate array per tab?
No. Derive each tab by filtering the one array at render time. Three stored arrays means three things to update on every change, and they will eventually disagree.
How do the tab counts stay correct?
They are derived from the same array, not tracked separately. Anything counted independently of the data it describes drifts from it the first time an update path is missed.

Related Challenges

Continue learning with these related challenges

View All
React

Seat Booking Grid

A cinema seat picker: rows at different prices, sold seats, a selection limit and a running total.

React · JavaScriptPratik Rai ·

React

Sequential Progress Bars

Clicking Add appends a progress bar, and the bars fill strictly one at a time. A queueing problem disguised as an animation.

React · JavaScriptPratik Rai ·

React

File Explorer

Build a nested folder tree from nothing — expand, collapse, and add files and folders at any depth.

React · JavaScriptPratik Rai ·