Skip to content

Repository files navigation

Todo React

Небольшое SPA для управления списком задач. Проект используется как учебное приложение для отработки архитектуры React-приложения, серверного состояния, маршрутизации, типизации, тестирования и оптимизации клиентского бандла.

Демо: https://sergey-bondarenko-dev.github.io/todo-react

Возможности

  • добавление, выполнение и удаление задач;
  • удаление всех задач через доступный диалог подтверждения;
  • поиск с регистронезависимой и безопасной HTML-подсветкой совпадений;
  • статистика выполненных задач;
  • состояния первоначальной загрузки и фонового обновления списка;
  • отображение ошибок запросов, повторная загрузка и уведомления об ошибках изменений;
  • прокрутка к первой незавершённой задаче;
  • отдельная страница задачи по маршруту /tasks/:id;
  • обработка неизвестных маршрутов;
  • анимация добавления и удаления элементов;
  • два источника данных: json-server и localStorage.

Стек

  • React 19 и TypeScript;
  • Vite 7;
  • React Router 8;
  • Redux Toolkit, RTK Query и React Redux;
  • Motion с LazyMotion;
  • Radix UI;
  • Sonner;
  • SCSS Modules;
  • ESLint и Stylelint;
  • Vitest, React Testing Library и jest-dom;
  • MSW и jsdom;
  • Playwright;
  • json-server;
  • gh-pages;
  • rollup-plugin-visualizer.

Архитектура

Код организован по принципам, близким к Feature-Sliced Design:

e2e/               # браузерные сквозные сценарии Playwright
src/
├── app/       # store, маршрутизация и глобальные стили
├── pages/     # страницы списка и отдельной задачи
├── widgets/   # композиция основного Todo-интерфейса
├── features/  # добавление, поиск и статистика
├── entities/  # RTK Query API и UI задачи
├── shared/    # API-адаптеры, UI-kit, типы, утилиты и ресурсы
└── test/      # общая настройка тестов, MSW server и Redux helpers

Компоненты не работают с хранилищем данных напрямую. Они используют сгенерированные RTK Query hooks, а endpoints обращаются к единому taskRepository. Репозиторий выбирает серверную или локальную реализацию через переменную окружения.

Ключевые решения

Серверное состояние через RTK Query

tasksApi описывает запросы списка и отдельной задачи, а также мутации добавления, переключения состояния и удаления. При добавлении задачи RTK Query ждёт ответ репозитория и пессимистично добавляет созданную задачу в кэш списка без повторного запроса. Переключение состояния оптимистично обновляет кэши списка и отдельной задачи, а при ошибке откатывает оба изменения. Инвалидация тегов синхронизирует данные после успешного переключения и удаления.

В Redux store зарегистрированы reducer и middleware RTK Query. Локальными React-состояниями остаются только UI-данные, например поисковая строка и значение формы.

Состояния запросов и обработка ошибок

HTTP-адаптер проверяет успешность ответа и во время выполнения валидирует структуру полученных задач. Ошибки HTTP, сети, JSON и структуры ответа приводятся к единому сериализуемому формату. Основной список и страница задачи различают первоначальную загрузку, фоновое обновление, ошибку и успешное состояние. При ошибке загрузку можно повторить, а уже полученные данные сохраняются на экране при неудачном фоновом запросе.

Ошибки добавления, изменения и удаления задач показываются через toast-уведомления. В статическом режиме отдельно обрабатываются недоступность localStorage и превышение его квоты. Удаление всех задач подтверждается с помощью доступного диалога на базе Radix UI.

React Router и GitHub Pages

Приложение использует declarative API React Router:

  • / — список задач;
  • /tasks/:id — подробности задачи;
  • * — страница 404.

BrowserRouter получает basename из import.meta.env.BASE_URL. Production-сборка использует /todo-react, поэтому маршруты корректно работают в подпапке GitHub Pages. Перед деплоем index.html копируется в 404.html, чтобы прямые переходы на вложенные маршруты возвращали SPA.

Два режима хранения данных

По умолчанию приложение обращается к json-server по адресу http://localhost:3001/tasks.

Если VITE_STATIC_BACKEND=true, используется адаптер localStorage. Оба адаптера реализуют один контракт TasksApi, поэтому RTK Query и UI не зависят от способа хранения. В .env.production статический режим включён автоматически.

Безопасная подсветка поиска

Перед передачей строки в dangerouslySetInnerHTML текст задачи экранируется как HTML, а поисковый запрос — как часть регулярного выражения. После этого найденные фрагменты оборачиваются в <mark>.

Анимация и размер бандла

Добавление и удаление TodoItem анимируются через AnimatePresence и облегчённый компонент m. Удаление начинается только после успешной мутации: RTK Query обновляет список, а AnimatePresence удерживает DOM-узел до завершения exit-анимации.

Вместо полного motion используется LazyMotion с динамически импортируемым domAnimation. Это выносит animation features в отдельный чанк и уменьшает начальный JavaScript. Настройка prefers-reduced-motion учитывается через Motion hook и глобальные стили.

Для просмотра состава бандла используется интерактивный treemap с raw, gzip и Brotli-размерами:

npm run build:analyze

Отчёт создаётся в dist/stats.html. Обычная production-сборка визуализатор не подключает.

Тестирование

Тесты запускаются через Vitest в окружении jsdom. Компонентные сценарии написаны с React Testing Library и user-event: проверки выполняются через доступные роли, подписи и наблюдаемое пользователем поведение.

MSW перехватывает HTTP-запросы на сетевой границе, поэтому интеграционные тесты используют реальные RTK Query endpoints, Redux store и API-адаптер вместо моков импортированных модулей. Для каждого теста создаётся отдельный store, а обработчики MSW сбрасываются между сценариями.

Браузерные e2e-тесты запускаются через Playwright в Chromium. Playwright автоматически поднимает Vite со статическим адаптером, а каждый сценарий выполняется в изолированном browser context со своим localStorage.

Тестами покрыты:

  • безопасная подсветка поиска и базовые UI-компоненты;
  • валидация и отправка формы добавления задачи;
  • чтение, runtime-валидация и мутации localStorage-репозитория;
  • запросы, нормализация ошибок, инвалидация и ручное обновление кэша RTK Query;
  • загрузка, поиск, повторный запрос, переключение и удаление задач;
  • подтверждение и отмена удаления всех задач;
  • ошибки мутаций и toast-уведомления;
  • маршрутизация и страница отдельной задачи;
  • сохранение добавления, выполнения и удаления задачи после перезагрузки страницы;
  • переход на страницу задачи и отображение неизвестного маршрута в реальном браузере.

Однократный запуск всего набора:

npm test

Режим наблюдения во время разработки:

npm run test:watch

Запуск e2e-тестов в headless Chromium:

npm run test:e2e

Интерактивный режим Playwright:

npm run test:e2e:ui

Требования

  • Node.js 22.22 или новее;
  • npm.

Установка и запуск

Установить зависимости:

npm install

Установить Chromium для запуска e2e-тестов:

npx playwright install chromium

Для разработки с HTTP API запустите два процесса.

Терминал 1:

npm run server

Терминал 2:

npm run dev

Dev-сервер будет доступен по адресу, который выведет Vite. json-server запускается на http://localhost:3001.

Запуск без json-server

PowerShell:

$env:VITE_STATIC_BACKEND='true'; npm run dev

Linux/macOS:

VITE_STATIC_BACKEND=true npm run dev

В этом режиме задачи сохраняются в localStorage текущего браузера.

Скрипты

Команда Назначение
npm run dev Запустить Vite dev server
npm run server Запустить json-server на порту 3001
npm run build Собрать production-версию
npm run build:analyze Собрать приложение и создать dist/stats.html
npm run preview Локально открыть production-сборку
npm run typecheck Проверить TypeScript
npm run lint Проверить TypeScript/TSX через ESLint
npm run lint:styles Проверить CSS/SCSS через Stylelint
npm test Однократно запустить тесты через Vitest
npm run test:watch Запустить Vitest в режиме наблюдения
npm run test:e2e Запустить e2e-тесты через Playwright в headless Chromium
npm run test:e2e:ui Открыть интерактивный режим Playwright
npm run deploy Собрать и опубликовать приложение через gh-pages

Проверка перед изменениями

npm run typecheck
npm run lint
npm run lint:styles
npm test
npm run test:e2e
npm run build

About

Todo-приложение на React 19 и TypeScript с React Router, RTK Query, двумя API-адаптерами, Motion-анимациями и FSD-подобной архитектурой.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages