← JavaScript/Проект: Todo App на React#381 из 383← ПредыдущийСледующий →+100 XP
Полезно по теме:Гайд: как учить JavaScriptПрактика: JS базаПрактика: async и сетьТермин: Closure

Капстоун проект: Todo App на React

О проекте

Это первый финальный проект для портфолио. Todo App — классический проект, который демонстрирует понимание основ React.

Что вы создадите:

  • Полнофункциональное приложение для задач
  • Локальное хранение в localStorage
  • Фильтрация и сортировка
  • Красивый адаптивный UI
  • Функциональные требования

    Основной функционал

    1. Создание задач

    - Текст задачи (обязательно)

    - Приоритет: низкий / средний / высокий

    - Дедлайн (опционально)

    2. Управление задачами

    - Отметить как выполненную

    - Редактировать текст

    - Удалить задачу

    3. Фильтрация

    - Все / Активные / Выполненные

    - По приоритету

    - Поиск по тексту

    4. Сортировка

    - По дате создания

    - По приоритету

    - По дедлайну

    Дополнительно (для портфолио)

  • Drag & Drop сортировка
  • Категории/теги
  • Статистика: выполнено сегодня, всего
  • Темная тема
  • PWA (работает офлайн)
  • Структура проекта

    src/
    ├── components/
    │   ├── TodoForm.jsx      # Форма создания
    │   ├── TodoItem.jsx      # Отдельная задача
    │   ├── TodoList.jsx      # Список задач
    │   ├── TodoFilters.jsx   # Фильтры и поиск
    │   └── TodoStats.jsx     # Статистика
    ├── hooks/
    │   ├── useTodos.js       # Логика управления
    │   └── useLocalStorage.js
    ├── utils/
    │   └── filters.js
    └── App.jsx

    Технологии

  • React 18+ с хуками
  • CSS Modules или Tailwind
  • localStorage для хранения
  • Опционально: React DnD для drag & drop
  • Критерии оценки

    | Критерий | Баллы |

    |----------|-------|

    | Работающий CRUD | 30 |

    | Фильтрация и поиск | 20 |

    | localStorage persistence | 15 |

    | Адаптивный UI | 15 |

    | Чистый код (компоненты) | 10 |

    | Дополнительные фичи | 10 |

    Пошаговый план

    Шаг 1: Структура данных

    const todo = {
      id: 'uuid',
      text: 'Изучить React',
      completed: false,
      priority: 'high', // 'low' | 'medium' | 'high'
      createdAt: '2024-01-15T10:00:00Z',
      deadline: '2024-01-20T23:59:59Z' // optional
    }

    Шаг 2: Custom Hook useTodos

    function useTodos() {
      const [todos, setTodos] = useState([])
      
      const addTodo = (text, priority, deadline) => { ... }
      const toggleTodo = (id) => { ... }
      const updateTodo = (id, updates) => { ... }
      const deleteTodo = (id) => { ... }
      
      return { todos, addTodo, toggleTodo, updateTodo, deleteTodo }
    }

    Шаг 3: Компоненты

    Разделите UI на независимые компоненты с чёткой ответственностью.

    Шаг 4: Сохранение в localStorage

    useEffect(() => {
      localStorage.setItem('todos', JSON.stringify(todos))
    }, [todos])

    Шаг 5: Фильтры и поиск

    Добавьте состояние для фильтров и примените их к списку.

    Чек-лист готовности

  • [ ] Можно создавать задачи
  • [ ] Можно отмечать выполненными
  • [ ] Можно редактировать и удалять
  • [ ] Фильтрация работает
  • [ ] Данные сохраняются после перезагрузки
  • [ ] UI адаптивный (mobile-friendly)
  • [ ] Код разделён на компоненты
  • [ ] Нет ошибок в консоли
  • Примеры

    Архитектура приложения: useTodos hook

    // Custom Hook для управления задачами
    
    function useTodos() {
      // Загрузка из localStorage при инициализации
      const [todos, setTodos] = React.useState(() => {
        const saved = localStorage.getItem('todos')
        return saved ? JSON.parse(saved) : []
      })
    
      // Сохранение в localStorage при изменении
      React.useEffect(() => {
        localStorage.setItem('todos', JSON.stringify(todos))
      }, [todos])
    
      // Создание задачи
      const addTodo = (text, priority = 'medium', deadline = null) => {
        const newTodo = {
          id: Date.now().toString(),
          text,
          completed: false,
          priority,
          deadline,
          createdAt: new Date().toISOString()
        }
        setTodos(prev => [newTodo, ...prev])
        return newTodo
      }
    
      // Переключение статуса
      const toggleTodo = (id) => {
        setTodos(prev => prev.map(todo =>
          todo.id === id ? { ...todo, completed: !todo.completed } : todo
        ))
      }
    
      // Обновление задачи
      const updateTodo = (id, updates) => {
        setTodos(prev => prev.map(todo =>
          todo.id === id ? { ...todo, ...updates } : todo
        ))
      }
    
      // Удаление задачи
      const deleteTodo = (id) => {
        setTodos(prev => prev.filter(todo => todo.id !== id))
      }
    
      // Очистка выполненных
      const clearCompleted = () => {
        setTodos(prev => prev.filter(todo => !todo.completed))
      }
    
      // Статистика
      const stats = {
        total: todos.length,
        completed: todos.filter(t => t.completed).length,
        active: todos.filter(t => !t.completed).length,
        highPriority: todos.filter(t => t.priority === 'high' && !t.completed).length
      }
    
      return {
        todos,
        addTodo,
        toggleTodo,
        updateTodo,
        deleteTodo,
        clearCompleted,
        stats
      }
    }
    
    // === Демонстрация ===
    console.log('=== useTodos Hook Demo ===\n')
    
    // Симуляция React-подобного state
    let todosState = []
    const setTodos = (fn) => {
      todosState = typeof fn === 'function' ? fn(todosState) : fn
    }
    
    // Функции
    const addTodo = (text, priority = 'medium') => {
      const newTodo = {
        id: Date.now().toString(),
        text,
        completed: false,
        priority,
        createdAt: new Date().toISOString()
      }
      setTodos(prev => [newTodo, ...prev])
      console.log('✅ Added:', text)
      return newTodo
    }
    
    const toggleTodo = (id) => {
      setTodos(prev => prev.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      ))
    }
    
    // Тестирование
    addTodo('Создать React проект', 'high')
    addTodo('Написать компоненты', 'medium')
    addTodo('Добавить стили', 'low')
    
    console.log('\nTodos:', todosState.map(t => 
      (t.completed ? '✅' : '⬜') + ' [' + t.priority + '] ' + t.text
    ))
    
    toggleTodo(todosState[0].id)
    console.log('\nПосле toggle:')
    console.log(todosState.map(t => 
      (t.completed ? '✅' : '⬜') + ' ' + t.text
    ))
    
    const stats = {
      total: todosState.length,
      completed: todosState.filter(t => t.completed).length,
      active: todosState.filter(t => !t.completed).length
    }
    console.log('\nStats:', stats)

    Капстоун проект: Todo App на React

    О проекте

    Это первый финальный проект для портфолио. Todo App — классический проект, который демонстрирует понимание основ React.

    Что вы создадите:

  • Полнофункциональное приложение для задач
  • Локальное хранение в localStorage
  • Фильтрация и сортировка
  • Красивый адаптивный UI
  • Функциональные требования

    Основной функционал

    1. Создание задач

    - Текст задачи (обязательно)

    - Приоритет: низкий / средний / высокий

    - Дедлайн (опционально)

    2. Управление задачами

    - Отметить как выполненную

    - Редактировать текст

    - Удалить задачу

    3. Фильтрация

    - Все / Активные / Выполненные

    - По приоритету

    - Поиск по тексту

    4. Сортировка

    - По дате создания

    - По приоритету

    - По дедлайну

    Дополнительно (для портфолио)

  • Drag & Drop сортировка
  • Категории/теги
  • Статистика: выполнено сегодня, всего
  • Темная тема
  • PWA (работает офлайн)
  • Структура проекта

    src/
    ├── components/
    │   ├── TodoForm.jsx      # Форма создания
    │   ├── TodoItem.jsx      # Отдельная задача
    │   ├── TodoList.jsx      # Список задач
    │   ├── TodoFilters.jsx   # Фильтры и поиск
    │   └── TodoStats.jsx     # Статистика
    ├── hooks/
    │   ├── useTodos.js       # Логика управления
    │   └── useLocalStorage.js
    ├── utils/
    │   └── filters.js
    └── App.jsx

    Технологии

  • React 18+ с хуками
  • CSS Modules или Tailwind
  • localStorage для хранения
  • Опционально: React DnD для drag & drop
  • Критерии оценки

    | Критерий | Баллы |

    |----------|-------|

    | Работающий CRUD | 30 |

    | Фильтрация и поиск | 20 |

    | localStorage persistence | 15 |

    | Адаптивный UI | 15 |

    | Чистый код (компоненты) | 10 |

    | Дополнительные фичи | 10 |

    Пошаговый план

    Шаг 1: Структура данных

    const todo = {
      id: 'uuid',
      text: 'Изучить React',
      completed: false,
      priority: 'high', // 'low' | 'medium' | 'high'
      createdAt: '2024-01-15T10:00:00Z',
      deadline: '2024-01-20T23:59:59Z' // optional
    }

    Шаг 2: Custom Hook useTodos

    function useTodos() {
      const [todos, setTodos] = useState([])
      
      const addTodo = (text, priority, deadline) => { ... }
      const toggleTodo = (id) => { ... }
      const updateTodo = (id, updates) => { ... }
      const deleteTodo = (id) => { ... }
      
      return { todos, addTodo, toggleTodo, updateTodo, deleteTodo }
    }

    Шаг 3: Компоненты

    Разделите UI на независимые компоненты с чёткой ответственностью.

    Шаг 4: Сохранение в localStorage

    useEffect(() => {
      localStorage.setItem('todos', JSON.stringify(todos))
    }, [todos])

    Шаг 5: Фильтры и поиск

    Добавьте состояние для фильтров и примените их к списку.

    Чек-лист готовности

  • [ ] Можно создавать задачи
  • [ ] Можно отмечать выполненными
  • [ ] Можно редактировать и удалять
  • [ ] Фильтрация работает
  • [ ] Данные сохраняются после перезагрузки
  • [ ] UI адаптивный (mobile-friendly)
  • [ ] Код разделён на компоненты
  • [ ] Нет ошибок в консоли
  • Примеры

    Архитектура приложения: useTodos hook

    // Custom Hook для управления задачами
    
    function useTodos() {
      // Загрузка из localStorage при инициализации
      const [todos, setTodos] = React.useState(() => {
        const saved = localStorage.getItem('todos')
        return saved ? JSON.parse(saved) : []
      })
    
      // Сохранение в localStorage при изменении
      React.useEffect(() => {
        localStorage.setItem('todos', JSON.stringify(todos))
      }, [todos])
    
      // Создание задачи
      const addTodo = (text, priority = 'medium', deadline = null) => {
        const newTodo = {
          id: Date.now().toString(),
          text,
          completed: false,
          priority,
          deadline,
          createdAt: new Date().toISOString()
        }
        setTodos(prev => [newTodo, ...prev])
        return newTodo
      }
    
      // Переключение статуса
      const toggleTodo = (id) => {
        setTodos(prev => prev.map(todo =>
          todo.id === id ? { ...todo, completed: !todo.completed } : todo
        ))
      }
    
      // Обновление задачи
      const updateTodo = (id, updates) => {
        setTodos(prev => prev.map(todo =>
          todo.id === id ? { ...todo, ...updates } : todo
        ))
      }
    
      // Удаление задачи
      const deleteTodo = (id) => {
        setTodos(prev => prev.filter(todo => todo.id !== id))
      }
    
      // Очистка выполненных
      const clearCompleted = () => {
        setTodos(prev => prev.filter(todo => !todo.completed))
      }
    
      // Статистика
      const stats = {
        total: todos.length,
        completed: todos.filter(t => t.completed).length,
        active: todos.filter(t => !t.completed).length,
        highPriority: todos.filter(t => t.priority === 'high' && !t.completed).length
      }
    
      return {
        todos,
        addTodo,
        toggleTodo,
        updateTodo,
        deleteTodo,
        clearCompleted,
        stats
      }
    }
    
    // === Демонстрация ===
    console.log('=== useTodos Hook Demo ===\n')
    
    // Симуляция React-подобного state
    let todosState = []
    const setTodos = (fn) => {
      todosState = typeof fn === 'function' ? fn(todosState) : fn
    }
    
    // Функции
    const addTodo = (text, priority = 'medium') => {
      const newTodo = {
        id: Date.now().toString(),
        text,
        completed: false,
        priority,
        createdAt: new Date().toISOString()
      }
      setTodos(prev => [newTodo, ...prev])
      console.log('✅ Added:', text)
      return newTodo
    }
    
    const toggleTodo = (id) => {
      setTodos(prev => prev.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      ))
    }
    
    // Тестирование
    addTodo('Создать React проект', 'high')
    addTodo('Написать компоненты', 'medium')
    addTodo('Добавить стили', 'low')
    
    console.log('\nTodos:', todosState.map(t => 
      (t.completed ? '✅' : '⬜') + ' [' + t.priority + '] ' + t.text
    ))
    
    toggleTodo(todosState[0].id)
    console.log('\nПосле toggle:')
    console.log(todosState.map(t => 
      (t.completed ? '✅' : '⬜') + ' ' + t.text
    ))
    
    const stats = {
      total: todosState.length,
      completed: todosState.filter(t => t.completed).length,
      active: todosState.filter(t => !t.completed).length
    }
    console.log('\nStats:', stats)

    Задание

    Создай полнофункциональный Todo App. Реализуй добавление, удаление, редактирование задач. Добавь фильтрацию по статусу и приоритету, поиск по тексту. Данные должны сохраняться в localStorage.

    Подсказка

    localStorage: JSON.stringify(todos). addTodo: [todo, ...prev]. toggleTodo: !todo.completed. deleteTodo: todo.id !== id.

    Загружаем среду выполнения...
    Загружаем AI-помощника...