Фронтенд-разработка: SASS, JavaScript и React

Курс поможет освоить современный фронтенд-стек: стилизацию с SASS, программирование на JavaScript и создание интерфейсов на React. Вы пройдёте путь от базовых концепций до построения компонентных приложений и интеграции с API.

1. Введение в фронтенд-стек и настройка рабочего окружения

Введение в фронтенд-стек и настройка рабочего окружения

Что такое фронтенд-стек в этом курсе

В этом курсе вы будете собирать пользовательский интерфейс (UI) для веб-приложений с помощью трёх ключевых технологий:

  • SASS — расширение CSS, которое добавляет переменные, вложенность, миксины и удобную организацию стилей.
  • JavaScript — язык, который «оживляет» интерфейс: логика, события, работа с данными и сетью.
  • React — библиотека для построения интерфейсов из компонентов и управления состоянием приложения.
  • Важно: React и SASS не заменяют базовые веб-технологии, а опираются на них. Поэтому параллельно вы будете закреплять фундаментальные понятия HTML/CSS/JS там, где это нужно для практики.

    !Схема того, как исходный код превращается в приложение в браузере

    Как будет устроена работа в проекте

    Современная фронтенд-разработка обычно выглядит так:

  • вы пишете код в редакторе
  • зависимости подключаются через менеджер пакетов
  • проект запускается локально через dev-сервер
  • сборщик пересобирает проект при изменениях
  • ошибки и поведение вы проверяете в браузере через DevTools
  • В этом курсе мы будем использовать связку: Node.js + npm + Vite + React + Sass.

    Что нужно установить

    Ниже — минимальный набор инструментов, чтобы комфортно проходить курс.

    Браузер и DevTools

  • Google Chrome или любой Chromium-браузер
  • DevTools (встроены)
  • Полезная документация:

  • Chrome DevTools
  • MDN Web Docs
  • Редактор кода

    Рекомендуемый вариант:

  • Visual Studio Code
  • Минимально полезные возможности редактора:

  • подсветка синтаксиса
  • встроенный терминал
  • форматирование кода
  • поиск по проекту
  • Git

    Git понадобится для контроля версий, истории изменений и отправки проектов.

  • Git
  • Проверка установки:

    Node.js и npm

    Node.js нужен для запуска инструментов сборки и менеджера пакетов, а npm обычно устанавливается вместе с Node.js.

  • Node.js
  • Проверка установки:

    Если у вас уже стоит Node.js, и вы не уверены, актуальная ли версия, в рамках курса ориентируйтесь на активные LTS-версии Node.js.

    Быстрый старт: создаём проект на React через Vite

    Почему Vite:

  • быстрый запуск dev-сервера
  • современная сборка
  • простая настройка
  • Ссылка на документацию:

  • Vite
  • Шаги создания проекта

  • Создайте папку под проекты и перейдите в неё.
  • Выполните команду создания React-проекта.
  • Перейдите в папку проекта и установите зависимости.
  • Запустите dev-сервер.
  • После запуска Vite покажет адрес (обычно http://localhost:5173). Откройте его в браузере.

    Добавляем SASS (SCSS) в проект

    Sass бывает в двух основных синтаксисах:

  • Sass (без фигурных скобок и точек с запятой)
  • SCSS (максимально похож на CSS)
  • В проектах на React чаще используют SCSS, поэтому в курсе будем использовать его.

    Документация:

  • Sass
  • Установка Sass

    Внутри проекта выполните:

    Подключение SCSS

  • Создайте файл стилей, например src/styles/main.scss.
  • Подключите его в src/main.jsx или в корневом компоненте.
  • Пример подключения в src/main.jsx:

    Проверьте, что стили применяются: добавьте в main.scss простой стиль и убедитесь, что он отразился в браузере.

    Как ориентироваться в структуре проекта

    У Vite + React проект обычно выглядит так:

  • index.html — входная HTML-страница
  • src/ — исходный код приложения
  • src/main.jsx — точка входа, где React «подключается» к странице
  • src/App.jsx — основной компонент приложения
  • package.json — зависимости и npm-скрипты
  • Полезно понимать, что браузер напрямую не «умеет» импортировать SCSS и JSX так же, как обычные файлы. Этим занимается инструментальная цепочка (Vite), которая преобразует исходники в то, что понимает браузер.

    npm-скрипты: что вы запускаете на самом деле

    В package.json есть раздел scripts. В типичном проекте Vite вы увидите примерно такие команды:

    | Команда | Что делает | Когда используется | |---|---|---| | npm run dev | Запускает dev-сервер | Во время разработки | | npm run build | Собирает продакшен-версию | Перед деплоем | | npm run preview | Локально показывает результат сборки | Проверить сборку перед публикацией |

    Минимальные основы отладки в браузере

    Чтобы быстрее учиться и исправлять ошибки, используйте DevTools:

  • Console: ошибки, console.log, предупреждения
  • Sources: точки останова (breakpoints) и пошаговое выполнение
  • Network: запросы к серверу и загрузка ресурсов
  • Elements: проверка HTML-структуры и применённых CSS-стилей
  • Если вы видите в консоли ошибку, начинайте с ответов на вопросы:

  • где именно ошибка (файл и строка)
  • что было undefined или «не является функцией»
  • что вы ожидали получить и что получили реально
  • Частые проблемы при настройке и как их диагностировать

  • node: command not found или npm: command not found
  • - Node.js не установлен или не добавлен в PATH
  • Ошибки установки пакетов
  • - проверьте, что вы в папке проекта и что есть package.json
  • Порт занят (dev-сервер не стартует)
  • - завершите процесс, который занял порт, или запустите Vite на другом порту
  • SCSS не применяется
  • - проверьте импорт файла и расширение (.scss), убедитесь, что sass установлен

    Что дальше по курсу

    После того как окружение настроено, мы будем двигаться так:

  • закрепим основы JavaScript, необходимые для интерфейсов
  • научимся структурировать стили с помощью SASS
  • перейдём к React: компоненты, props, state, события
  • добавим работу с данными и типичные паттерны фронтенд-приложений
  • Ключевой результат этого урока: у вас должен запускаться локальный React-проект, а SCSS должен корректно подключаться и обновляться при изменениях.

    2. SASS: переменные, вложенность, миксины и архитектура стилей

    SASS: переменные, вложенность, миксины и архитектура стилей

    Зачем SASS в курсе и как он связан с React-проектом

    В предыдущем уроке вы настроили окружение и подключили sass к проекту на Vite + React. Теперь задача — писать стили так, чтобы они:

  • были переиспользуемыми
  • легко поддерживались
  • не превращались в «комок» из конфликтующих селекторов
  • Sass (SCSS) расширяет CSS возможностями, которые особенно полезны в компонентной разработке: переменные, вложенность, миксины и удобная архитектура файлов.

    Документация:

  • Sass
  • SCSS как надстройка над CSS

    SCSS — это синтаксис Sass, максимально похожий на обычный CSS. Почти любой валидный CSS-файл является валидным SCSS.

    Ключевая идея: вы пишете SCSS, а сборщик (в нашем случае Vite) компилирует его в обычный CSS, который понимает браузер.

    Переменные: единый источник правды для цветов, размеров и отступов

    Базовые переменные

    Переменные в SCSS начинаются со знака color-primary: #4f46e5; space-3: 12px;

    .button { background: radius-md; padding: space-3 понятнее, чем «случайные 12px»

    Переменные как дизайн-токены

    На практике переменные часто отражают систему дизайна:

  • цвета (font-, space-*)
  • радиусы (shadow-*)
  • Такой подход делает стили предсказуемыми и облегчает поддержку.

    Карты (maps) для группировки значений

    Если значений много, их удобно хранить в карте.

    Здесь map-get(gap: 8px) { display: flex; align-items: center; justify-content: center; gap: breakpoints: ( sm: 640px, md: 768px, lg: 1024px );

    @mixin up(value: map-get(name); @media (min-width: color-primary: #4f46e5; radius-md;

    &:focus-visible { @include a.focus-ring; } } scss * { box-sizing: border-box; }

    body { margin: 0; font-family: system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif; color: #111827; background: #f9fafb; }

    img { max-width: 100%; display: block; } scss @use "./abstracts";

    @use "./base/global";

    @use "./components/button"; @use "./components/card"; jsx export function Button({ variant = 'primary', children }) { const className = variant === 'danger' ? 'button button--danger' : 'button'

    return ( <button className={className} type="button"> {children} </button> ) } scss @use "../abstracts" as a;

    .button { padding: 10px 14px; border: 0; border-radius: a.color-primary; cursor: pointer;

    &:focus-visible { @include a.focus-ring; }

    &--danger { background: #dc2626; } } `

    Важно: SCSS может быть глобальным, но логика переиспользования и структура файлов всё равно позволяют держать проект в порядке.

    Частые ошибки и как их избегать

  • Слишком глубокая вложенность
  • Смешивание токенов и конкретных компонентов в одном файле
  • Использование «магических» значений вместо переменных
  • Переизбыток @extend, который усложняет итоговый CSS
  • Ориентир на практике: простая структура файлов, явные зависимости через @use`, переменные для повторяемых значений и миксины для повторяемых паттернов.

    Что дальше

    Теперь у вас есть инструменты, чтобы писать стили масштабируемо. В следующих уроках мы будем чаще применять это в связке с JavaScript и React: состояния, события, условные классы, стилизация компонентов и работа с данными.

    3. JavaScript: основы языка, DOM и события

    JavaScript: основы языка, DOM и события

    Как этот урок связан с предыдущими

    В предыдущих уроках вы:

  • настроили проект на Vite + React и подключили SCSS
  • научились организовывать стили через токены, миксины и структуру файлов
  • Теперь добавляем третий ключевой слой фронтенда — JavaScript. Именно он отвечает за:

  • поведение интерфейса
  • работу с данными
  • реакцию на действия пользователя
  • Даже если вы планируете писать UI в React, понимание JavaScript, DOM и событий остаётся обязательным: React работает поверх этих основ.

    Где выполняется JavaScript в вашем проекте

    JavaScript исполняется в двух типичных средах:

  • в браузере — ваш UI, DOM, события, запросы
  • в Node.js — инструменты разработки (Vite, npm-скрипты, сборка)
  • В этом уроке мы фокусируемся на браузерном JavaScript.

    Базовый синтаксис и стиль кода

    let, const и почему var почти не нужен

  • const — значение нельзя переназначить
  • let — можно переназначить
  • var — старый вариант со сложными правилами области видимости
  • Важно: const запрещает переназначение переменной, но не делает объект неизменяемым.

    Типы данных, которые вы будете встречать постоянно

  • string — строки
  • number — числа
  • boolean — логика
  • null — явное «пусто»
  • undefined — значение не задано
  • object — объекты, массивы, функции
  • Проверка типа:

    Приведение типов и «ложные» значения

    В JavaScript встречаются значения, которые в условии считаются ложными:

  • false
  • 0
  • ''
  • null
  • undefined
  • NaN
  • Практическое правило: в условиях пишите проверки явно, если это влияет на смысл.

    Функции: основа интерактивности

    Объявление функции и стрелочная функция

    Параметры по умолчанию

    Колбэки

    Колбэк — это функция, которую вы передаёте как аргумент другой функции.

    События в браузере почти всегда работают через колбэки.

    Объекты и массивы: хранение данных

    Объекты

    Объект — это набор пар ключ: значение.

    Деструктуризация

    Массивы и полезные методы

    Методы map, filter, reduce особенно важны в React, потому что UI часто строится из массивов данных.

    Модули: как код делится на файлы

    Современные проекты используют ES-модули:

  • export — что отдаём из файла
  • import — что забираем в другом файле
  • src/utils/math.js:

    src/main.js:

    В Vite такие импорты работают «из коробки».

    DOM: как JavaScript видит страницу

    DOM — это объектная модель документа: браузер представляет HTML как дерево узлов, и JavaScript может читать и менять это дерево.

    !DOM как дерево узлов, которым управляет JavaScript

    Поиск элементов

    Чаще всего используют querySelector и querySelectorAll.

    Важно:

  • querySelector возвращает первый найденный элемент или null
  • querySelectorAll возвращает NodeList, по нему можно итерироваться
  • Изменение содержимого и атрибутов

    Практические подсказки:

  • textContent безопаснее, когда вы хотите вставить именно текст
  • для классов используйте classList, а не ручную конкатенацию строк
  • Создание и вставка элементов

    События: как UI реагирует на пользователя

    Событие — это сигнал от браузера: клик, ввод текста, отправка формы, прокрутка.

    addEventListener

    Частые события:

  • click
  • input
  • submit
  • keydown
  • Объект события event

    Обработчик получает объект события.

    Полезные поля:

  • event.target — элемент, который инициировал событие
  • event.currentTarget — элемент, на котором висит обработчик
  • preventDefault на формах

    Если вы слушаете submit, браузер по умолчанию перезагрузит страницу. Для SPA это обычно не нужно.

    Всплытие событий и делегирование

    События обычно всплывают от вложенного элемента к родителям. Это позволяет использовать делегирование: один обработчик на контейнер вместо множества обработчиков на каждый элемент списка.

    Здесь:

  • мы повесили один обработчик на список
  • клики по любому элементу внутри списка обрабатываются через поиск ближайшего .todo-item
  • Асинхронность: запросы к серверу и fetch

    Интерфейсы часто получают данные по сети, и это происходит асинхронно.

    fetch и async/await

    Что важно понять:

  • await «ждёт» промис и возвращает результат
  • обработку ошибок удобно делать через try/catch внутри async функции или через .catch
  • Документация:

  • MDN: fetch
  • MDN: async function
  • Как это связано с React

    React берёт на себя часть работы с DOM, но основы остаются теми же:

  • вместо document.createElement вы описываете UI через JSX
  • вместо ручного addEventListener вы используете обработчики в компонентах, например onClick
  • вместо прямых изменений DOM через classList вы меняете состояние, а React обновляет DOM сам
  • Понимание DOM и событий помогает:

  • увереннее отлаживать UI в DevTools
  • понимать, что такое всплытие событий и почему иногда обработчик срабатывает «не там»
  • грамотно работать с формами, вводом и сетевыми запросами
  • Мини-практика в рамках урока: страница без React внутри вашего проекта

    Даже в React-проекте полезно уметь воспроизвести базовую механику на чистом JS. Например, вы можете создать отдельный файл, подключаемый временно, и потренироваться:

  • найти элемент
  • подписаться на событие
  • изменить текст или класс
  • И затем сравнить: как то же самое делается в React через состояние и props.

    Документация по базовым темам урока:

  • MDN: JavaScript guide
  • MDN: Введение в DOM
  • MDN: addEventListener
  • 4. JavaScript: асинхронность, работа с API и модули

    JavaScript: асинхронность, работа с API и модули

    Связь с предыдущими уроками

    Ранее вы настроили проект на Vite + React и подключили SCSS, а затем разобрали основы JavaScript, DOM и события. Теперь мы добавляем три темы, без которых современный фронтенд почти не бывает:

  • Асинхронность: как JavaScript выполняет задачи, которые занимают время.
  • Работа с API: как получать и отправлять данные по сети.
  • Модули: как раскладывать код по файлам и собирать приложение из частей.
  • Эти темы напрямую понадобятся в React: компоненты часто загружают данные, обрабатывают события и используют импорт функций и компонентов из разных файлов.

    !Схема того, как асинхронные операции попадают в очереди и когда выполняются

    Что такое асинхронность в JavaScript

    Асинхронность нужна, когда операция не может завершиться мгновенно:

  • сетевой запрос fetch
  • таймер setTimeout
  • чтение файлов (обычно в Node.js)
  • ожидание ответа пользователя
  • Ключевая цель: не блокировать главный поток, чтобы интерфейс оставался отзывчивым.

    Синхронный код и проблема блокировки

    Синхронный код выполняется сверху вниз, строка за строкой.

    Если между A и B появится долгий расчёт или ожидание сети, то B и C «зависнут», а вместе с ними может «зависнуть» и UI.

    Промисы: базовый инструмент асинхронности

    Промис (Promise) — это объект, который представляет результат асинхронной операции.

    У промиса есть состояния:

  • pending — ожидание
  • fulfilled — выполнено успешно
  • rejected — выполнено с ошибкой
  • Документация:

  • MDN: Promise
  • Создание и использование промиса

    Здесь:

  • resolve(value) переводит промис в успешное состояние и передаёт результат
  • .then(handler) получает результат успешного выполнения
  • Обработка ошибок: .catch и .finally

    Важно понимать разницу:

  • .catch ловит ошибки промиса
  • .finally выполняется всегда и не получает результат запроса как аргумент
  • Параллельные запросы: Promise.all

    Promise.all полезен, когда нужно дождаться нескольких независимых запросов.

    Особенность: если любой промис завершится с ошибкой, то ошибка будет у всего Promise.all.

    Документация:

  • MDN: Promise.all
  • async/await: синхронный стиль для асинхронного кода

    async/await — это синтаксис поверх промисов, который делает код более читаемым.

    Документация:

  • MDN: async function
  • MDN: await
  • Пример с try/catch

    GET-запрос с параметрами

    Параметры в URL лучше собирать через URLSearchParams.

    src/main.jsx или любой другой файл:

    Правила:

  • имя в фигурных скобках должно совпадать с именем экспорта
  • путь обычно начинается с ./ или ../
  • Экспорт по умолчанию и его цена

    default export позволяет импортировать под любым именем.

    src/utils/logger.js:

    Импорт:

    Практическое правило: для библиотек внутри проекта часто удобнее именованные экспорты, потому что они делают импорты более явными и упрощают рефакторинг.

    Реэкспорт: сборка публичного API модуля

    Иногда удобно сделать файл, который «собирает» экспорты.

    src/api/index.js:

    Тогда импорт становится короче:

    Изоляция ответственности: пример структуры

    Вместо того чтобы писать fetch прямо в компонентах, часто делают слой API.

  • src/api/ — запросы к серверу
  • src/utils/ — утилиты
  • src/components/ — UI
  • Это уменьшает связность: UI-компоненты знают что загрузить, но не обязаны знать как именно устроен HTTP-запрос.

    Как это применится в React в следующих уроках

    Когда вы перейдёте к React, схема обычно выглядит так:

  • компонент запускает загрузку данных при появлении
  • данные сохраняются в состоянии
  • в зависимости от состояния показываются загрузка, ошибка или результат
  • Асинхронность, fetch и модули дают фундамент для этого подхода: вы умеете получать данные, обрабатывать ошибки и держать код аккуратно разложенным по файлам.

    Короткий чек-лист по качеству кода

  • Проверяйте response.ok перед response.json()
  • Оборачивайте await в try/catch, если ошибка должна быть обработана
  • Выносите сетевые запросы в отдельные модули src/api/*
  • Избегайте «магических строк» URL по всему проекту, храните базовый URL в одном месте
  • Думайте об отмене запросов для интерактивных сценариев
  • 5. React: компоненты, JSX, props и state

    React: компоненты, JSX, props и state

    Как этот урок связан с предыдущими

    Ранее вы настроили проект на Vite + React и подключили Sass, а также разобрали JavaScript: синтаксис, события, работу с API и модули. Теперь мы переходим к практической основе React-разработки: научимся собирать интерфейс из компонентов, описывать UI через JSX, передавать данные через props и управлять изменениями через state.

    React не отменяет знания JavaScript, а использует их напрямую:

  • компоненты и хуки это обычные функции
  • обработчики событий это колбэки
  • списки и преобразования данных обычно пишутся через map и filter
  • разбиение по файлам делается ES-модулями import и export
  • Официальная документация React:

  • React Learn
  • Что такое React

    React это библиотека для построения пользовательских интерфейсов.

    Ключевая идея React: вы описываете что должно быть на экране для текущих данных, а React сам обновляет DOM, когда данные меняются.

    Практический результат для разработчика:

  • меньше ручной работы с DOM
  • интерфейс становится предсказуемее: UI это функция от данных
  • приложение удобно собирать из переиспользуемых частей
  • Компоненты: базовые строительные блоки

    Компонент в React это функция, которая возвращает описание интерфейса.

    Функциональный компонент

    Простейший компонент:

    Правила, которые важно запомнить:

  • имя компонента пишется с заглавной буквы: Hello, UserCard
  • компонент возвращает JSX
  • компоненты можно экспортировать и импортировать как обычные функции
  • Пример импорта компонента:

    Композиция компонентов

    Обычно интерфейс строят не одним большим компонентом, а композицией.

    !Дерево компонентов, показывающее как UI собирается из частей

    Идея композиции:

  • App собирает страницу из крупных блоков
  • крупные блоки внутри используют более мелкие компоненты
  • один компонент может переиспользоваться в разных местах
  • JSX: разметка внутри JavaScript

    JSX это синтаксис, похожий на HTML, который позволяет описывать интерфейс прямо в JavaScript.

    Документация:

  • Writing Markup with JSX
  • Что важно понимать про JSX

  • JSX это не строка и не HTML, это синтаксис, который преобразуется в вызовы React на этапе сборки
  • внутри JSX можно вставлять значения JavaScript через фигурные скобки {}
  • Пример выражений в JSX:

    Один корневой элемент

    Компонент должен вернуть один корневой узел. Если нужно вернуть несколько соседних элементов, используйте фрагмент.

    Отличия атрибутов JSX от HTML

    Частые отличия:

  • class заменяется на className
  • for в label заменяется на htmlFor
  • Пример:

    Условный рендеринг

    В React вы обычно не показываете и не прячете элементы через прямое управление DOM. Вместо этого вы условно возвращаете JSX.

    Частые варианты:

  • оператор &&
  • тернарный оператор условие ? A : B
  • Списки и key

    Чтобы отрисовать список, чаще всего используют map.

    key это специальный проп, который помогает React понимать, какой элемент списка соответствует какому объекту данных при изменениях списка.

    Практическое правило: key должен быть стабильным идентификатором элемента, обычно это id из данных.

    Props: входные данные компонента

    Props это объект с входными данными, которые передаются компоненту извне.

    Документация:

  • Passing Props to a Component
  • Props нельзя менять внутри компонента

    Props считаются неизменяемыми со стороны компонента-получателя: компонент должен использовать их для построения UI, но не менять.

    Пример компонента карточки:

    Использование:

    children: содержимое между тегами

    children это проп, который содержит вложенный контент.

    Использование:

    Передача обработчиков событий через props

    Частый паттерн: родитель хранит данные и передаёт дочернему компоненту функцию, которую надо вызвать по событию.

    Это помогает держать логику там, где находится состояние, и переиспользовать UI-компоненты.

    State: состояние и обновление интерфейса

    State это данные, которые принадлежат конкретному компоненту и могут изменяться со временем. Когда state меняется, React перерисовывает компонент.

    Документация:

  • State: A Component's Memory
  • useState
  • useState на практике

    useState это хук, который возвращает пару:

  • текущее значение состояния
  • функцию для его обновления
  • Почему нельзя изменять state напрямую

    State нужно обновлять только через функцию-сеттер. Если изменить значение напрямую, React может не понять, что нужно обновить интерфейс.

    Плохой подход:

    Правильный подход:

    Функциональное обновление состояния

    Иногда следующее значение зависит от предыдущего. Тогда безопаснее передавать в сеттер функцию.

    Такой вариант устойчивее, когда обновления происходят подряд или внутри нескольких событий.

    State для объектов и массивов: обновляйте иммутабельно

    Если состояние это массив или объект, обычно создают новую копию, а не меняют старую.

    Пример добавления элемента в массив:

    Управляемые инпуты

    Когда значение поля ввода хранится в state, это называется управляемый компонент.

    Связь с предыдущим уроком про DOM и события:

  • onChange и onClick это обработчики событий
  • e.preventDefault() работает так же, как в обычном JavaScript
  • вместо document.querySelector и textContent вы меняете state, а React обновляет DOM
  • Поток данных: props вниз, события вверх

    Один из самых важных принципов React: данные обычно идут сверху вниз через props.

  • родитель хранит state
  • дочерние компоненты получают данные через props
  • дочерние компоненты сообщают о действиях через колбэки
  • !Однонаправленный поток данных: props вниз, события вверх

    Как это стыкуется с Sass из предыдущих уроков

    SCSS продолжает работать так же, просто классы вы применяете в JSX.

  • статический класс: className="card"
  • условный класс: выбираете строку на основе props или state
  • Пример условного класса:

    Здесь подход из Sass-урока про модификаторы (&--danger) удобно сочетается с логикой React.

    Частые ошибки новичков

  • Смешивание ответственности: компонент одновременно и грузит данные, и содержит много разметки, и хранит сложное состояние
  • Пропуск key в списках или использование индекса массива как key без понимания последствий
  • Попытка изменять props или state напрямую
  • Ожидание, что setState мгновенно изменит значение в текущей строке кода
  • Что дальше по курсу

    Следующий логичный шаг после компонентов, JSX, props и state:

  • разбирать жизненный цикл через хуки и эффекты
  • загружать данные по сети и хранить состояния loading и error
  • проектировать структуру приложения: где хранить данные, как организовать папки и модули
  • В следующих уроках вы будете использовать знания об асинхронности и API из JavaScript-части курса, но уже в React-компонентах.

    6. React: хуки, маршрутизация и управление состоянием

    React: хуки, маршрутизация и управление состоянием

    Как этот урок связан с предыдущими

    Вы уже умеете:

  • писать компоненты на React с JSX
  • передавать данные через props
  • хранить локальное состояние через useState
  • работать с асинхронностью и API в JavaScript
  • подключать и организовывать стили через SCSS
  • Теперь мы соберём эти навыки в более реалистичную картину приложения:

  • разберём ключевые хуки React и правила их использования
  • добавим маршрутизацию для SPA через React Router
  • обсудим подходы к управлению состоянием: локальное, поднятое, через Context, через useReducer
  • Основная цель урока: научиться строить приложение не как один экран, а как набор страниц и модулей с предсказуемым потоком данных.

    Хуки React: что это и зачем

    Хук в React это функция, которая позволяет функциональному компоненту подключать возможности React, например состояние и эффекты.

    Ключевой принцип: компонент остаётся обычной функцией, а хуки дают ему память и реакцию на изменения.

    Официальная документация:

  • React Hooks
  • Правила хуков

    Есть два главных правила:

  • вызывать хуки только на верхнем уровне компонента или внутри своего хука
  • вызывать хуки только из React-функций: компонентов или кастомных хуков
  • Почему так: React связывает вызовы хуков с порядком их вызова. Если вы начнёте вызывать useState или useEffect внутри if или внутри цикла, порядок может меняться между рендерами и React потеряет соответствие.

    useEffect: побочные эффекты, данные и жизненный цикл

    useEffect нужен для побочных эффектов: действий, которые выходят за пределы чистого построения UI.

    Типичные эффекты:

  • загрузка данных fetch
  • подписка на события
  • работа с таймерами
  • синхронизация с localStorage
  • Документация:

  • useEffect
  • Базовый пример

    Что здесь важно:

  • функция, переданная в useEffect, запускается после отрисовки
  • второй аргумент [] означает, что эффект запускается один раз при монтировании
  • return () => ... это очистка эффекта, она сработает при размонтировании
  • Зависимости эффекта

    Второй аргумент useEffect это массив зависимостей. Он говорит React, когда нужно перезапускать эффект.

    | Зависимости | Когда запускается эффект | |---|---| | нет второго аргумента | после каждого рендера | | [] | один раз при монтировании | | [a, b] | при монтировании и при изменении a или b |

    Практическое правило: всё, что вы используете внутри эффекта из внешней области видимости компонента, обычно должно быть в зависимостях.

    Загрузка данных и отмена запроса

    Для загрузки данных часто нужно состояние загрузки и ошибки.

    Такой хук можно использовать в нескольких компонентах, не копируя логику.

    Маршрутизация в React: страницы в SPA

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

    В React чаще всего используют React Router.

    Документация:

  • React Router
  • !Поясняет, как URL сопоставляется компонентам и как работают вложенные маршруты

    Установка

    Базовая настройка маршрутов

    Один из распространённых вариантов это BrowserRouter + Routes.

    src/main.jsx:

    src/App.jsx:

    Что здесь происходит:

  • Layout это обёртка для общих элементов страницы
  • index маршрут это главная страница внутри /
  • users/:userId это динамический сегмент маршрута
  • * это обработка несуществующих страниц
  • Layout и Outlet

    src/pages/Layout.jsx:

    Outlet это место, куда React Router подставит компонент текущего вложенного маршрута.

    Ссылки: Link и NavLink

  • Link делает переход без перезагрузки страницы
  • NavLink умеет определять активный маршрут и удобно связывается со стилями
  • Вместо <a href="/users"> в SPA обычно используют Link или NavLink, чтобы не терять состояние приложения.

    Документация:

  • Link
  • NavLink
  • Параметры маршрута: useParams

    src/pages/UserDetails.jsx:

    Управление состоянием: какие бывают уровни

    Состояние в React бывает разного масштаба.

    !Помогает выбрать, где хранить состояние и как оно течёт по дереву

    Локальное состояние

    Хранится в компоненте и нужно только ему.

  • открыта ли конкретная модалка
  • текст в поле ввода внутри формы
  • Инструмент: useState.

    Поднятое состояние

    Если состояние нужно нескольким компонентам, его поднимают в ближайшего общего родителя.

    Признаки, что пора поднимать состояние:

  • два соседних компонента должны видеть одни и те же данные
  • один компонент меняет данные, а другой должен обновиться
  • Это основной паттерн React: props вниз, события вверх.

    Глобальное состояние

    Если данные нужны многим частям приложения на разных уровнях, прокидывать props становится неудобно. Тогда используют:

  • React Context
  • связку Context + useReducer
  • Документация:

  • Context
  • useContext
  • Context: общий доступ к данным без прокидывания props

    Context позволяет передать значение глубоко в дерево, минуя явную передачу через props на каждом уровне.

    Пример: тема приложения

    src/state/theme.js:

    Подключение провайдера в main.jsx:

    Использование в компоненте:

    Практическое правило: Context отлично подходит для редко меняющихся глобальных данных, например тема, локаль, данные пользователя.

    Context + useReducer: предсказуемое глобальное состояние

    Если глобальное состояние часто меняется и имеет много сценариев изменения, сочетание Context и useReducer делает логику более структурированной.

    Идея:

  • reducer описывает, как состояние меняется от действий
  • Context раздаёт { state, dispatch } всем заинтересованным компонентам
  • Это не единственный вариант управления глобальным состоянием, но он встроен в React и хорошо подходит для учебных и средних проектов.

    Практика структуры проекта

    Один из рабочих вариантов разложения файлов:

  • src/pages/ страницы для маршрутов
  • src/components/ переиспользуемые UI-компоненты
  • src/api/ функции для сетевых запросов
  • src/state/ контексты, редьюсеры, кастомные хуки состояния
  • src/styles/ SCSS архитектура из предыдущих уроков
  • Плюс такого разбиения: UI, сеть и состояние меньше смешиваются.

    Как связать маршрутизацию, состояние и SCSS

    Типичный сценарий:

  • маршруты определяют, какие страницы отображаются
  • страница загружает данные через useEffect или через кастомный хук
  • состояние загрузки и ошибки влияет на то, какой JSX возвращается
  • активные ссылки в меню подсвечиваются через NavLink и модификаторный класс, который удобно описывать в SCSS как &--active
  • Частые ошибки и как их избегать

  • бесконечные перезапуски useEffect из-за неправильных зависимостей
  • хранение производных данных в state вместо вычисления из исходных данных
  • попытка использовать Context как замену всей архитектуры, вместо точечного решения проблемы проп-дриллинга
  • преждевременная оптимизация через useMemo и useCallback без измерения
  • Что дальше

    После хуков, маршрутизации и базового управления состоянием вы готовы к более прикладным темам:

  • загрузка данных на страницах с обработкой loading и error
  • разделение приложения на модули и переиспользуемые компоненты
  • более устойчивые решения для данных, которые приходят с сервера
  • 7. Сборка проекта: тестирование, оптимизация и деплой

    Сборка проекта: тестирование, оптимизация и деплой

    Зачем нужен отдельный этап сборки

    В предыдущих уроках вы собирали приложение на Vite + React, подключали SCSS, работали с асинхронностью и маршрутизацией. До этого момента вы в основном жили в режиме разработки: быстрый dev-сервер, удобная отладка, частые правки.

    Но реальный продукт проходит ещё три обязательных шага:

  • тестирование — чтобы изменения не ломали существующее поведение
  • оптимизация — чтобы приложение быстро загружалось и работало
  • деплой — чтобы приложение стало доступно пользователям
  • !Схема того, как код проходит путь от коммита до публикации

    Сборка в Vite: что происходит при npm run build

    В типичном Vite-проекте есть три ключевые команды:

    | Команда | Назначение | Что важно понимать | |---|---|---| | npm run dev | разработка | максимально быстрые обновления, не про оптимизацию | | npm run build | продакшен-сборка | минификация, оптимизация, сборка ассетов | | npm run preview | проверка сборки локально | запускает сервер, который отдаёт уже собранный dist/ |

    Документация:

  • Vite: Build
  • Vite: Preview
  • Что такое папка dist

    После npm run build Vite создаёт папку dist/ со статическими файлами:

  • HTML
  • JS-бандлы
  • CSS
  • картинки и шрифты
  • Именно эту папку обычно деплоят на хостинг.

    Почему важно прогонять npm run preview

    Dev-режим может скрывать некоторые проблемы:

  • отличия путей к файлам
  • особенности работы роутинга на статическом хостинге
  • ошибки, проявляющиеся только в минифицированном коде
  • Поэтому перед деплоем полезно:

  • собрать проект npm run build
  • проверить сборку npm run preview
  • Переменные окружения в Vite (для API и режимов)

    Частая ситуация: в разработке у вас один URL API, в продакшене другой. В Vite это решается через переменные окружения.

    Правила Vite:

  • в клиентский код попадают только переменные, начинающиеся с VITE_
  • доступ из кода через import.meta.env
  • Документация:

  • Vite: Env Variables and Modes
  • Пример .env.development:

    Пример использования:

    Практическое правило: не хардкодьте URL API по всему проекту. Вынесите базовый URL в одно место (например, src/config/env.js) и используйте в модулях src/api/*.

    Тестирование: что именно тестировать во фронтенде

    Тестирование во фронтенде обычно делят на уровни:

  • юнит-тесты — проверяют небольшую функцию или модуль (например, форматирование цены)
  • компонентные тесты — проверяют React-компонент: что рендерится и как реагирует на события
  • E2E (end-to-end) — проверяют сценарий целиком в браузере (например, логин и переход по страницам)
  • Важно: тесты не должны дублировать реализацию. Они должны проверять контракт поведения.

    Юнит- и компонентные тесты: Vitest

    Для Vite самый естественный выбор — Vitest.

    Документация:

  • Vitest
  • Установка:

    Пример добавления скриптов в package.json:

  • vitest удобен в режиме разработки (watch)
  • vitest run удобен для CI, потому что выполняется один раз и завершает процесс
  • Тестирование React-компонентов: React Testing Library

    Подход React Testing Library: тестируйте компонент так, как его использует пользователь.

    Документация:

  • React Testing Library
  • Установка:

    Пример теста компонента счётчика (идея из уроков про useState и события):

    Здесь проверяется поведение:

  • кнопка доступна по роли
  • клик меняет то, что видит пользователь
  • E2E-тесты: Playwright (опционально, но полезно)

    E2E-тесты имеют смысл, когда у вас есть несколько страниц (React Router), формы, запросы к API.

    Документация:

  • Playwright
  • Playwright обычно подключают позже, когда приложение уже содержит несколько основных сценариев.

    Оптимизация: что реально ускоряет React-приложение

    Оптимизация должна опираться на замеры и здравый смысл: сначала вы решаете проблемы сборки и доставки файлов, затем оптимизируете рендер.

    Базовые оптимизации, которые делает сборка

    При npm run build Vite включает типичные оптимизации:

  • минификация JS и CSS
  • разделение кода на чанки (в зависимости от зависимостей)
  • хэширование имён файлов для кеширования браузером
  • Code splitting: загрузка кода по требованию

    Когда у вас есть роуты (урок про React Router), полезно грузить страницы лениво.

    Документация:

  • React: lazy
  • React: Suspense
  • Пример:

    Что это даёт:

  • код страницы /users не попадает в начальный бандл
  • первая загрузка приложения становится легче
  • Оптимизация рендера: используйте хуки по назначению

    Из урока про хуки важно вынести практичное правило:

  • useMemo и useCallback не ускоряют приложение сами по себе
  • они помогают, если у вас действительно есть дорогие вычисления или лишние перерендеры из-за нестабильных ссылок
  • Если вы не измеряли проблему, чаще всего достаточно:

  • держать состояние ближе к месту использования
  • не хранить производные значения в state
  • избегать лишней глубины дерева компонентов и лишних глобальных контекстов
  • Анализ размера бандла

    Если сборка стала тяжёлой, используйте визуализатор.

    Документация:

  • rollup-plugin-visualizer
  • Установка:

    Подключение в vite.config.js:

    После npm run build у вас появится отчёт, где видно:

  • какие зависимости самые тяжёлые
  • что попало в начальный бандл
  • есть ли дубли
  • Деплой: как опубликовать Vite + React приложение

    В большинстве учебных и многих реальных проектов деплой делается как публикация статических файлов dist/.

    Документация:

  • Vite: Static Deploy
  • Важный момент для SPA с React Router

    Если вы используете BrowserRouter, то при прямом открытии адреса вида /users/123 сервер должен отдать тот же index.html, а уже React Router разберёт маршрут на клиенте.

    На статических хостингах это решается правилами редиректа или fallback-настройкой.

    Netlify

    Документация:

  • Netlify: Deploy React
  • Типовая настройка:

  • Build command: npm run build
  • Publish directory: dist
  • Для SPA-роутинга часто добавляют файл public/_redirects:

    Vercel

    Документация:

  • Vercel: Deploy Vite
  • Обычно Vercel сам определяет настройки, но ключевое то же:

  • build: npm run build
  • output: dist
  • GitHub Pages

    GitHub Pages удобен для портфолио, но требует внимания к настройке базового пути.

    Документация:

  • Vite: GitHub Pages
  • Если репозиторий публикуется не в корне домена, а в формате https://username.github.io/repo/, нужно указать base.

    Пример vite.config.js:

    Практическое правило: если в GitHub Pages у вас ломаются пути к ассетам, почти всегда причина в неправильном base.

    CI: автоматизация проверки и сборки через GitHub Actions

    Минимальная автоматизация обычно включает:

  • установка зависимостей
  • линт и тесты
  • сборка
  • Документация:

  • GitHub Actions
  • Пример workflow .github/workflows/ci.yml:

    Почему в CI лучше npm ci, а не npm install:

  • npm ci устанавливает зависимости строго по lock-файлу и делает сборку более воспроизводимой
  • Документация:

  • npm: npm ci
  • Практичный чек-лист перед публикацией

  • npm run build проходит без ошибок
  • npm run preview открывает приложение и основные страницы работают
  • сетевые запросы используют переменные окружения, а не хардкод
  • для SPA настроен fallback на index.html
  • тяжёлые страницы грузятся лениво (если приложение уже заметно выросло)
  • тесты запускаются локально и в CI
  • Что дальше

    На этом этапе вы умеете пройти полный цикл фронтенд-разработки:

  • написать компоненты и стили
  • загрузить данные и организовать состояние
  • собрать продакшен-версию
  • проверить её тестами
  • оптимизировать загрузку и структуру бандла
  • задеплоить и автоматизировать проверку
  • Этот набор навыков превращает учебный проект в полноценный публичный проект в портфолио, который не стыдно показывать и развивать дальше.