Todo Application with Advanced Features
Master machine coding by creating a feature-rich Todo application with filtering, persistence, real-time updates, and modern React patterns.
Machine Coding Challenge: Advanced Todo Application
Create a production-ready Todo application that demonstrates advanced React patterns, state management, and real-world UX considerations.
Problem Statement
Design and implement a Todo application with the following requirements:
✅ Core Features (Must Have)
- Add, edit, and delete todos
- Mark todos as complete/incomplete
- Filter todos by status (All, Active, Completed)
- Persist todos in localStorage
- Responsive design for mobile and desktop
Advanced Features (Nice to Have)
- Real-time search with debouncing
- Bulk actions (select all, delete selected)
- Drag and drop reordering
- Due date functionality with reminders
- Dark/Light theme toggle
- Keyboard shortcuts support
UI/UX Requirements
- Clean, modern interface
- Smooth animations and transitions
- Loading states and error handling
- Empty state design
- Accessibility compliance (ARIA labels, keyboard navigation)
Clarifying Questions & Scope
Before diving into implementation, clarify these aspects:
Functional Requirements
- Todo Structure: What fields should each todo have? (title, description, due date, priority, tags?)
- Filtering: Should we support multiple filters simultaneously?
- Persistence: localStorage only, or should we simulate API calls?
- Real-time: Should changes sync across multiple browser tabs?
Technical Constraints
- Time Limit: 2-3 hours for implementation
- Framework: React with hooks (no external state management initially)
- Styling: CSS-in-JS, Tailwind, or plain CSS?
- Browser Support: Modern browsers only or IE11+?
Success Criteria
- Clean, maintainable code structure
- Proper error handling and edge cases
- Responsive and accessible design
- Performance optimization for large lists
High-Level Architecture
1. Component Structure
TodoApp/
├── TodoHeader/ # Search, filters, bulk actions
├── TodoList/ # Main todo container
│ ├── TodoItem/ # Individual todo component
│ └── TodoEmpty/ # Empty state component
├── TodoForm/ # Add/edit todo form
├── TodoFilters/ # Filter controls
└── TodoStats/ # Progress indicators2. State Management Strategy
interface Todo {
id: string;
title: string;
description?: string;
completed: boolean;
createdAt: Date;
dueDate?: Date;
priority: 'low' | 'medium' | 'high';
tags: string[];
}
interface AppState {
todos: Todo[];
filter: 'all' | 'active' | 'completed';
searchQuery: string;
selectedTodos: Set<string>;
theme: 'light' | 'dark';
isLoading: boolean;
}3. Data Flow
User Action → Event Handler → State Update → Re-render → UI Update
↓
localStorage Sync ← Persistence Layer ← State Change🛠️ Step-by-Step Implementation
Step 1: Project Setup & Basic Structure
// App.tsx
import React, { useState, useEffect } from 'react';
import TodoHeader from './components/TodoHeader';
import TodoList from './components/TodoList';
import TodoForm from './components/TodoForm';
import { useLocalStorage } from './hooks/useLocalStorage';
import { Todo, FilterType } from './types';
const App: React.FC = () => {
const [todos, setTodos] = useLocalStorage<Todo[]>('todos', []);
const [filter, setFilter] = useState<FilterType>('all');
const [searchQuery, setSearchQuery] = useState('');
const [selectedTodos, setSelectedTodos] = useState<Set<string>>(new Set());
// Filtered and searched todos
const filteredTodos = todos
.filter(todo => {
if (filter === 'active') return !todo.completed;
if (filter === 'completed') return todo.completed;
return true;
})
.filter(todo =>
todo.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
todo.description?.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 transition-colors">
<div className="max-w-4xl mx-auto p-6">
<TodoHeader
searchQuery={searchQuery}
onSearchChange={setSearchQuery}
filter={filter}
onFilterChange={setFilter}
selectedCount={selectedTodos.size}
onBulkDelete={() => {/* implementation */}}
/>
<TodoForm onAddTodo={addTodo} />
<TodoList
todos={filteredTodos}
selectedTodos={selectedTodos}
onTodoToggle={toggleTodo}
onTodoDelete={deleteTodo}
onTodoEdit={editTodo}
onTodoSelect={toggleTodoSelection}
/>
</div>
</div>
);
};Step 2: Custom Hooks for Reusability
// hooks/useLocalStorage.ts
import { useState, useEffect } from 'react';
export function useLocalStorage<T>(
key: string,
initialValue: T
): [T, (value: T | ((val: T) => T)) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(`Error reading localStorage key "${key}":`, error);
return initialValue;
}
});
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(`Error setting localStorage key "${key}":`, error);
}
};
return [storedValue, setValue];
}Step 3: Core Components Implementation
// components/TodoForm.tsx
import React, { useState } from 'react';
interface TodoFormProps {
onAddTodo: (todo: Omit<Todo, 'id' | 'createdAt'>) => void;
}
const TodoForm: React.FC<TodoFormProps> = ({ onAddTodo }) => {
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!title.trim()) return;
onAddTodo({
title: title.trim(),
description: description.trim(),
completed: false,
priority: 'medium',
tags: [],
});
setTitle('');
setDescription('');
};
return (
<form onSubmit={handleSubmit} className="mb-6">
<div className="flex gap-4">
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="What needs to be done?"
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
required
/>
<button
type="submit"
className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Add Todo
</button>
</div>
</form>
);
};Step 4: Advanced Features Implementation
// hooks/useDebounce.ts
import { useState, useEffect } from 'react';
export function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
// components/TodoHeader.tsx
import React from 'react';
import { useDebounce } from '../hooks/useDebounce';
interface TodoHeaderProps {
searchQuery: string;
onSearchChange: (query: string) => void;
filter: FilterType;
onFilterChange: (filter: FilterType) => void;
selectedCount: number;
onBulkDelete: () => void;
}
const TodoHeader: React.FC<TodoHeaderProps> = ({
searchQuery,
onSearchChange,
filter,
onFilterChange,
selectedCount,
onBulkDelete,
}) => {
const debouncedSearchQuery = useDebounce(searchQuery, 300);
return (
<div className="mb-6 space-y-4">
{/* Search Input */}
<div className="relative">
<input
type="text"
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
placeholder="Search todos..."
className="w-full px-4 py-2 pl-10 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500"
/>
<SearchIcon className="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" />
</div>
{/* Filters and Bulk Actions */}
<div className="flex items-center justify-between">
<div className="flex gap-2">
{(['all', 'active', 'completed'] as const).map((filterType) => (
<button
key={filterType}
onClick={() => onFilterChange(filterType)}
className={`px-4 py-2 rounded-lg transition-colors ${
filter === filterType
? 'bg-blue-600 text-white'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
{filterType.charAt(0).toUpperCase() + filterType.slice(1)}
</button>
))}
</div>
{selectedCount > 0 && (
<button
onClick={onBulkDelete}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
>
Delete Selected ({selectedCount})
</button>
)}
</div>
</div>
);
};UI/UX Enhancements
Responsive Design
/* Tailwind classes for responsive design */
.todo-container {
@apply max-w-4xl mx-auto p-4 md:p-6;
}
.todo-item {
@apply flex items-center gap-4 p-4 bg-white rounded-lg shadow-sm hover:shadow-md transition-shadow;
}
@media (max-width: 768px) {
.todo-item {
@apply flex-col items-start gap-2;
}
}Accessibility Features
// Enhanced TodoItem with accessibility
const TodoItem: React.FC<TodoItemProps> = ({ todo, onToggle, onDelete, onEdit }) => {
return (
<div
role="listitem"
className="todo-item"
aria-label={`Todo: ${todo.title}`}
>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
aria-label={`Mark "${todo.title}" as ${todo.completed ? 'incomplete' : 'complete'}`}
className="w-5 h-5 text-blue-600 rounded focus:ring-blue-500"
/>
<span
className={`flex-1 ${todo.completed ? 'line-through text-gray-500' : ''}`}
>
{todo.title}
</span>
<div className="flex gap-2">
<button
onClick={() => onEdit(todo)}
aria-label={`Edit "${todo.title}"`}
className="p-2 text-gray-600 hover:text-blue-600 transition-colors"
>
<EditIcon className="w-4 h-4" />
</button>
<button
onClick={() => onDelete(todo.id)}
aria-label={`Delete "${todo.title}"`}
className="p-2 text-gray-600 hover:text-red-600 transition-colors"
>
<DeleteIcon className="w-4 h-4" />
</button>
</div>
</div>
);
};Performance Optimizations
Memoization for Large Lists
// Optimized TodoList with React.memo
const TodoItem = React.memo<TodoItemProps>(({ todo, onToggle, onDelete, onEdit }) => {
// Component implementation
});
// Virtual scrolling for very large lists
import { FixedSizeList as List } from 'react-window';
const VirtualizedTodoList: React.FC<{ todos: Todo[] }> = ({ todos }) => {
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
<div style={style}>
<TodoItem todo={todos[index]} />
</div>
);
return (
<List
height={600}
itemCount={todos.length}
itemSize={80}
width="100%"
>
{Row}
</List>
);
};Efficient State Updates
// Optimized state updates
const addTodo = useCallback((newTodo: Omit<Todo, 'id' | 'createdAt'>) => {
const todo: Todo = {
...newTodo,
id: generateId(),
createdAt: new Date(),
};
setTodos(prev => [...prev, todo]);
}, []);
const toggleTodo = useCallback((id: string) => {
setTodos(prev =>
prev.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
)
);
}, []);Key Takeaways
What You've Learned
- Component Architecture: Breaking complex UIs into logical, reusable components
- State Management: Efficient data flow with React hooks and custom hooks
- Performance Optimization: Memoization, virtualization, and efficient updates
- User Experience: Responsive design, accessibility, and smooth interactions
- Code Quality: Clean, maintainable, and testable code structure
Common Pitfalls to Avoid
- ❌ Over-engineering: Don't add complexity unless required
- ❌ Poor State Management: Avoid prop drilling and inefficient updates
- ❌ Ignoring Edge Cases: Always handle empty states and error conditions
- ❌ Accessibility Neglect: Ensure keyboard navigation and screen reader support
- ❌ Performance Issues: Optimize for large datasets and smooth interactions
Next Steps
- Implement drag and drop functionality
- Add real-time collaboration features
- Integrate with a backend API
- Add comprehensive test coverage
- Deploy to production environment
This Todo application demonstrates the core principles of modern React development and serves as an excellent foundation for more complex applications. The patterns and techniques you've learned here will be invaluable in your machine coding interviews and real-world projects!