Создание сайта на React с нуля

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

1. Что такое React и как он устроен

Что такое React и как он устроен

React — это JavaScript-библиотека для создания пользовательских интерфейсов (UI). Её ключевая идея: описывать интерфейс как набор компонентов, каждый из которых отвечает за небольшой фрагмент страницы и может переиспользоваться.

Зачем React

React помогает:

  • Разбивать интерфейс на независимые части (компоненты) и собирать из них страницу.
  • Поддерживать состояние UI (например, выбранные фильтры, открытые модальные окна) без ручного «дерганья» DOM.
  • Делать обновления интерфейса предсказуемыми: данные меняются → интерфейс автоматически пересчитывается.
  • Компонентная модель

    Компонент — это функция (чаще всего), которая получает входные данные и возвращает описание того, что должно быть отображено.

    Мысленно интерфейс можно представить деревом:

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

    JSX: «шаблоны», которые на самом деле JavaScript

    В React часто используют JSX — синтаксис, похожий на HTML, но это не HTML. JSX — это удобная запись вызовов функций React, которая компилируется в JavaScript.

    Важно помнить:

  • В JSX можно встраивать выражения JavaScript в фигурных скобках {...}.
  • Атрибуты немного отличаются от HTML (например, className вместо class).
  • JSX должен возвращать один корневой элемент (или фрагмент).
  • Как React обновляет экран: Virtual DOM и reconciler

    Браузерная страница представлена DOM-деревом. Напрямую менять DOM вручную на больших страницах сложно: легко забыть обновить какой-то элемент, и появляются ошибки состояния UI.

    React делает иначе:

  • На основе текущих данных React строит виртуальное представление интерфейса (часто называют Virtual DOM — это не «магический DOM», а обычные JS-объекты, описывающие структуру).
  • При изменении данных React строит новое виртуальное представление.
  • Затем сравнивает старое и новое (процесс называется reconciliation, «согласование»).
  • После сравнения React применяет к реальному DOM только минимально необходимые изменения.
  • Смысл: вы описываете «как должно выглядеть», а React решает «что именно изменить в браузере».

    Props и State: входные данные и внутреннее состояние

    В React есть два основных источника данных:

  • Props (пропсы) — входные данные компонента, передаются сверху вниз от родителя к дочернему.
  • - Пропсы считаются «только для чтения»: компонент не должен менять то, что ему передали.
  • State (состояние) — внутренние данные компонента, которые могут меняться со временем (например, текст в поле ввода, открыто/закрыто меню).
  • Типичный поток данных: родитель хранит состояние → передает дочерним через props → дочерние вызывают обработчик (функцию) → родитель обновляет state → UI пересчитывается.

    Хуки (Hooks): как компонент «подключается» к возможностям React

    В функциональных компонентах React использует хуки — специальные функции, позволяющие:

  • Хранить состояние (useState).
  • Выполнять эффекты (запросы, подписки) (useEffect).
  • Получать данные из контекста (useContext) и др.
  • Правило, которое стоит запомнить сразу: хуки вызывают на верхнем уровне компонента, не внутри if, циклов и вложенных функций. Это нужно, чтобы React стабильно понимал порядок хуков между рендерами.

    Рендер и эффекты: что происходит «когда данные изменились»

    Упрощённая модель работы:

  • Компонент рендерится: React вызывает функцию компонента и получает описание UI.
  • React обновляет DOM (если нужно).
  • Затем React выполняет эффекты (например, код в useEffect).
  • Полезная мысль: рендер — это расчёт, а не обязательно «перерисовка всего на экране». Перерисовывается только то, что реально изменилось.

    Списки и ключи: как React понимает, что именно изменилось

    Когда вы отображаете список элементов, React важно знать, какой элемент «тот же самый» между рендерами. Для этого используется key.

  • key должен быть уникальным среди соседних элементов списка.
  • Лучше всего подходит стабильный идентификатор из данных (например, id).
  • Индекс массива как key часто приводит к визуальным и логическим ошибкам при вставках/удалениях.
  • Композиция вместо наследования

    В React принято собирать интерфейс через композицию:

  • Маленькие компоненты объединяются в более крупные.
  • Логику и разметку можно «вкладывать» через children.
  • Общие части выносятся в отдельные компоненты.
  • Это обычно проще и гибче, чем иерархии наследования, привычные по ООП.

    ---

    Задания для закрепления

  • Объясните своими словами разницу между props и state и приведите по одному примеру каждого.
  • Опишите шаги, как React обновляет интерфейс при изменении состояния (в 3–5 пунктов).
  • Почему key важен в списках? Что может пойти не так, если использовать индекс массива как key?
  • Что такое рендер в React и почему «рендер» не означает «перерисовать всю страницу»?
  • <details> <summary> Ответы </summary>

  • Props — входные данные от родителя, компонент их не изменяет. Пример: UserCard получает name и avatarUrl. State — внутренние данные компонента, которые меняются со временем. Пример: isModalOpen внутри компонента модального окна.
  • Примерный процесс:
  • 1) меняются данные (обычно state), 2) React заново вызывает компонент и строит новое виртуальное дерево, 3) сравнивает новое и старое (reconciliation), 4) применяет минимальные изменения к реальному DOM, 5) запускает эффекты (например, из useEffect).

  • key помогает React сопоставлять элементы списка между рендерами. Если поставить индекс как key, то при вставке/удалении в середине списка элементы «переедут», и React может неправильно сохранить состояние элементов (например, текст в инпуте окажется в другой строке).
  • Рендер — это вычисление описания UI (вызов компонента и построение виртуального дерева). Это не равняется полной перерисовке: React обновляет в DOM только то, что реально изменилось после сравнения.
  • </details>

    2. Установка Node.js и создание проекта (Vite)

    Установка Node.js и создание проекта (Vite)

    Чтобы работать с React-проектом, нужен Node.js (с ним устанавливаются инструменты сборки, зависимости и dev‑сервер). В этой статье вы установите Node.js, проверите окружение и создадите новый проект на Vite.

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

  • Node.js (LTS) — рекомендуемая стабильная версия.
  • npm — менеджер пакетов (обычно ставится вместе с Node.js).
  • Проверка после установки (в терминале):

    Если команды не находятся — Node.js не установился или не добавился в PATH.

    2) Установка Node.js

    Windows

  • Скачайте установщик Node.js версии LTS.
  • Установите с настройками по умолчанию.
  • Перезапустите терминал (PowerShell/Command Prompt) и выполните:
  • Если видите версии — всё готово.

    macOS

    Варианты:

  • Установить Node.js LTS через официальный установщик.
  • Если у вас менеджер версий (например, nvm), можно поставить Node через него — это удобно, если у разных проектов разные версии Node.
  • Проверьте версии:

    Linux

    Рекомендуется ставить Node.js через менеджер пакетов вашей системы или через менеджер версий (например, nvm). После установки проверьте:

    3) Быстрая диагностика окружения

    Где вы сейчас находитесь

    Перед созданием проекта убедитесь, что вы в нужной папке:

    Права доступа (частая проблема на macOS/Linux)

    Если при установке пакетов видите ошибки прав, не ставьте зависимости «глобально» и не используйте sudo без понимания причины. Чаще всего помогает:

  • Работать внутри своей домашней папки.
  • Использовать менеджер версий Node (он ставит Node локально для пользователя).
  • 4) Создание проекта React на Vite

    Vite создаёт структуру проекта, настраивает сборку и запускает dev‑сервер.

    Создать проект

    В терминале перейдите в папку, где хотите хранить проекты, и выполните одну из команд.

    JavaScript-шаблон React:

    TypeScript-шаблон React (рекомендуется, если хотите типизацию):

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

    Запустить dev‑сервер

    Терминал покажет локальный адрес (обычно что-то вроде http://localhost:5173). Откройте его в браузере.

    5) Что именно создаёт Vite (минимально необходимое)

    Типичная структура выглядит так:

    Коротко по смыслу:

  • package.json — список зависимостей и команд (скриптов).
  • src/ — ваш исходный код.
  • main.jsx/tsx — точка входа приложения.
  • App.jsx/tsx — основной компонент приложения (детали компонентов и JSX были в предыдущей статье).
  • 6) Полезные команды проекта

    Они лежат в package.json в секции scripts и запускаются через npm run ...:

  • Запуск разработки:
  • Сборка для публикации (оптимизированные файлы):
  • Локальный просмотр результата сборки:
  • После build обычно появляется папка dist/ — её содержимое и является статическим сайтом.

    7) Частые проблемы и быстрые решения

    1) node/npm не найден

  • Закройте и заново откройте терминал.
  • Проверьте, что Node установлен.
  • 2) Зависимости не ставятся или проект “сломался” после прерывания установки

    3) Порт занят

  • Vite обычно сам предложит другой порт.
  • Или завершите процесс, который занял порт, и запустите снова.
  • ---

    Задания для закрепления

  • Установите Node.js (LTS) и убедитесь, что команды node -v и npm -v работают.
  • Создайте проект Vite с шаблоном react или react-ts.
  • Запустите npm run dev и откройте страницу в браузере.
  • Откройте package.json и найдите, какие команды доступны в scripts.
  • Соберите проект командой npm run build, затем запустите npm run preview.
  • <details> <summary> Ответы </summary>

    1) Проверка установки:

    Обе команды должны вывести версии.

    2) Создание проекта:

    3) Запуск:

    В терминале появится адрес localhost — его нужно открыть в браузере.

    4) В package.json ищите блок:

    5) Сборка и предпросмотр:

    build создаёт папку dist/, а preview поднимает локальный сервер, который показывает именно результат сборки.

    </details>

    3. Компоненты и JSX: собираем страницы сайта

    Компоненты и JSX: собираем страницы сайта

    Когда проект на Vite уже создан и запускается, следующий шаг — научиться собирать страницу из компонентов. В прошлой статье про React уже были основы JSX, дерево компонентов и идея props/state. Здесь сосредоточимся на практике: как разбивать макет на части, писать удобные компоненты и компоновать их в страницу.

    1) Как «разрезать» страницу на компоненты

    Хорошее правило: компонент — это кусок интерфейса, который:

  • Имеет понятные границы (шапка, подвал, карточка, секция).
  • Может переиспользоваться (в этом проекте или позже).
  • Имеет простую ответственность (не делает «всё сразу»).
  • Пример разбиения типичного лендинга:

    2) Организация файлов (удобная «скелетная» структура)

    Один из простых вариантов внутри src/:

    Почему так удобно:

  • components/ — переиспользуемые UI-блоки.
  • pages/ — крупные «страницы», которые собирают секции.
  • App.jsx — связывает страницу и общий каркас.
  • 3) Пишем базовые компоненты (Layout-подход)

    Container: обёртка с children

    Компоненты-обёртки помогают держать одинаковые отступы и ширину контента.

    Header и Footer: простая разметка + настройка через props

    4) Компонент страницы: собираем секции

    Страница — это обычно компонент, который не пытается быть универсальным, а просто собирает блоки в нужном порядке.

    Дальше подключаем всё в App.jsx:

    5) Частые приёмы JSX при сборке страниц

    Условный вывод

    1) Через && — когда нужно показать блок только при условии:

    2) Через тернарный оператор — когда есть два варианта:

    3) «Ранний возврат» — когда компоненту нечего рисовать:

    Передача обработчиков (без лишней логики внутри JSX)

    Аккуратная переиспользуемость: меньше «жёсткого текста»

    Если блок повторяется, выносите в компонент и параметризуйте props:

    6) Мини-чеклист качества компонента

  • Компонент читается «сверху вниз» без лишних ветвлений.
  • Название отражает смысл (FeatureCard, Header, Section).
  • В props передаются данные, а не «кусок глобального состояния».
  • Внутри компонента минимум хардкода, который мешает переиспользованию.
  • ---

    Задания для закрепления

  • Разбейте макет страницы на 5–7 компонентов (например: Header, Hero, Features, FeatureCard, Contact, Footer). Нарисуйте дерево компонентов в виде текста.
  • Сделайте компонент Section({ title, children }) и используйте его минимум 2 раза на странице.
  • Сделайте компонент Header, который принимает brand и массив links и рендерит навигацию через map.
  • Добавьте условный рендеринг: если links пустой, навигация не показывается.
  • <details> <summary> Ответы </summary>

    1) Пример дерева:

    2) Пример Section:

    3) Пример Header с links.map(...):

    4) Условие для навигации:

    </details>

    4. Стилизация: CSS, модули и базовая адаптивность

    Стилизация: CSS, модули и базовая адаптивность

    React не диктует способ стилизации: вы выбираете подход, который удобно поддерживать. В рамках простого сайта на Vite чаще всего достаточно обычного CSS + CSS Modules для изоляции стилей компонентов.

    1) Глобальные стили: база для всего сайта

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

    Обычно делают файл:

    Подключение — в точке входа (main.jsx), один раз:

    Пример содержимого globals.css:

    Почему переменные полезны: вы меняете цвет/ширину контейнера в одном месте — обновляется весь сайт.

    2) Обычный CSS для повторяемых утилит (например, контейнер)

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

    3) CSS Modules: изоляция стилей компонента

    CSS Modules — это обычный CSS, но классы автоматически становятся «локальными» для компонента. В Vite достаточно назвать файл *.module.css.

    Пример структуры рядом с компонентом:

    Header.module.css:

    Header.jsx (идея подключения):

    Ключевая мысль: в JSX вы используете styles.className, а не строку "className".

    Частая задача: несколько классов

    Иногда нужно добавить класс при условии (например, активная ссылка). Без библиотек можно собрать строку так:

    4) Когда выбирать глобальный CSS, а когда модули

    Удобное правило:

  • Глобально (globals.css):
  • - базовые стили (body, a, типографика) - CSS-переменные - утилиты вроде .container
  • CSS Modules:
  • - всё, что относится к конкретному компоненту (шапка, карточка, секция)

    Так стили не «протекают» между компонентами и меньше случайных конфликтов.

    5) Базовая адаптивность: чтобы сайт выглядел нормально на телефоне

    Адаптивность начинается с двух принципов:

  • Резиновая ширина: используйте max-width + margin: 0 auto + боковые padding (контейнер).
  • Mobile-first: стили для узких экранов — по умолчанию, расширение — через @media (min-width: ...).
  • Пример: навигация в шапке на широком экране — в строку, на узком — переносится или становится колонкой.

    Ещё два практичных совета:

  • Используйте относительные единицы (rem, %), а не фиксированные px для всего подряд (отступы и шрифты легче «масштабируются»).
  • Проверяйте адаптивность в DevTools (режим устройства): ищите горизонтальный скролл — его обычно создают элементы шире экрана.
  • 6) Мини-чеклист «стили читаются и не ломаются»

  • Классы компонентов — через CSS Modules.
  • Общие значения (цвета, ширины, отступы) — через :root переменные.
  • Контент не упирается в края экрана (есть padding у контейнера).
  • На ширине ~360–390px ничего не вылезает за экран.
  • ---

    Задания для закрепления

  • Создайте src/styles/globals.css, подключите его в main.jsx и добавьте CSS-переменные (--accent, --container).
  • Перенесите стили Header в Header.module.css и подключите через import styles from "./Header.module.css".
  • Добавьте для ссылок два состояния: обычное и активное (через второй класс). Протестируйте, что активный стиль применяется условно.
  • Сделайте простую адаптивность: на ширине меньше 480px навигация в шапке должна переноситься на новую строку или становиться колонкой.
  • <details> <summary> Ответы </summary>

    1) Глобальные стили:

  • main.jsx:
  • globals.css:
  • 2) CSS Modules для Header:

  • файл должен называться Header.module.css
  • подключение:
  • использование:
  • 3) Условный активный класс:

    4) Адаптивность навигации (один из вариантов):

    Если не хотите колонку — можно оставить flex-direction: row, но добавить flex-wrap: wrap;, чтобы ссылки переносились.

    </details>

    5. Состояние и события: делаем интерфейс интерактивным

    Состояние и события: делаем интерфейс интерактивным

    Интерактивный интерфейс в React строится вокруг простой связки:

  • State хранит текущие данные UI (открыто/закрыто, выбранная вкладка, текст в поле).
  • События (клик, ввод, отправка формы) меняют state.
  • React пересчитывает разметку на основе нового state.
  • Базовые понятия props/state и условный рендеринг уже были в предыдущих статьях — здесь сосредоточимся на практических паттернах.

    1) useState на практике: обновления, которые не ломаются

    Простой интерактив: кнопка «лайк».

    Когда нужно обновлять «от предыдущего значения»

    Если новое значение вычисляется из старого, используйте функциональное обновление — это защищает от ошибок при серии быстрых обновлений.

    Практический пример: «+2» (две операции подряд).

    2) События в React: что важно помнить

    В JSX вы подписываетесь на события через onClick, onChange, onSubmit и т.д.

  • Передавайте функцию, а не результат вызова: onClick={handleClick}, не onClick={handleClick()}.
  • Объект события приходит аргументом: function handle(e) { ... }.
  • Отмена отправки формы

    По умолчанию браузер перезагружает страницу при submit. В React это почти всегда не нужно.

    3) Управляемые поля (controlled components): форма как часть state

    Для сайта часто нужны формы (контакты, подписка). Самый предсказуемый подход — хранить значения полей в state.

    Ключевая идея: value/checked берутся из state, а изменения приходят через onChange.

    4) Состояние объектов и массивов: обновляйте неизменяемо

    React ожидает, что вы не мутируете state “на месте”. Для массивов и объектов создавайте новую копию.

    Пример: список задач (добавление/удаление)

    Важно: key берите из id, а не из индекса массива (почему — обсуждали ранее).

    5) «Поднять состояние вверх»: один источник правды

    Если нескольким компонентам нужно одно и то же состояние (например, активная вкладка и её содержимое), храните state в ближайшем общем родителе.

    Паттерн: родитель хранит state → передаёт данные и обработчики вниз через props.

    6) Частые ошибки и быстрые проверки

  • Мутация state: items.push(...) или obj.x = 1 — плохо. Делайте копии.
  • Дублирование “вычисляемого состояния”: если значение можно посчитать из других данных — лучше считать прямо в рендере.
  • Ожидание мгновенного обновления: после setState не полагайтесь на “уже обновилось”. Если нужно значение “из прошлого” — используйте функциональный вариант setX((prev) => ...).
  • ---

    Задания для закрепления

  • Сделайте компонент Counter, где есть кнопки +1, -1, Сброс, а также кнопка +2 (двойное увеличение без багов).
  • Сделайте форму подписки: поле email, чекбокс согласия и кнопка отправки. Кнопка должна быть отключена, пока email пустой или согласие не поставлено.
  • Сделайте список “ссылки шапки”: поле ввода названия пункта и кнопка “Добавить”. Ниже — список пунктов с кнопкой удаления.
  • Сделайте табы (2–3 вкладки). Активная вкладка должна подсвечиваться классом.
  • <details> <summary> Ответы </summary>

    1) Counter:

    2) Отключение кнопки:

    3) Список пунктов (идея): храните массив объектов {id, label}; добавляйте через setItems((p) => [newItem, ...p]); удаляйте через filter.

    4) Подсветка активной вкладки (без библиотек):

    </details>

    6. Маршрутизация: многостраничный сайт на React Router

    Маршрутизация: многостраничный сайт на React Router

    Обычный React‑сайт на Vite — это SPA: браузер загружает одну HTML‑страницу, а «переходы» между разделами делает JavaScript без полной перезагрузки. За это отвечает маршрутизация: сопоставление адресов (/, /about, /blog/123) и компонентов, которые нужно показать.

    Для React самый популярный инструмент — React Router.

    1) Установка React Router

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

    После установки у вас появятся компоненты и хуки для маршрутов.

    2) Базовая настройка роутера

    Обычно роутер подключают в точке входа (где рендерится App). Внутри BrowserRouter объявляют список маршрутов:

    Ключевые идеи:

  • path — какой адрес обрабатывает маршрут.
  • element — какой компонент показывать.
  • path="*" — «всё остальное», обычно страница 404.
  • Маршрут без path, но с element — удобный способ сделать общий layout для нескольких страниц.
  • 3) Layout‑маршрут и <Outlet />

    Layout нужен, чтобы шапка/подвал были общими, а внутри менялся контент.

    <Outlet /> — место, куда React Router «вставит» компонент текущей страницы.

    Визуально:

    4) Переходы: <Link /> вместо <a />

    Для внутренних переходов используйте Link, чтобы не перезагружать страницу.

    <a href="/about"> тоже откроет страницу, но сделает полную перезагрузку и «сломает» SPA‑ощущение.

    Активная ссылка: <NavLink />

    Если нужно подсвечивать текущий пункт меню:

    isActive будет true, когда текущий адрес совпадает с маршрутом.

    5) Динамические маршруты: параметры URL

    Типичный пример — страница статьи/товара по id: /posts/123.

    Маршрут:

    Чтение параметра внутри страницы:

    postId — это строка из адреса. Если дальше будете искать элемент в массиве по числовому id, приводите тип явно.

    6) Query‑параметры: поиск, фильтры

    Для адресов вида /posts?tag=react&page=2 используйте useSearchParams.

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

    7) Редиректы и «защита» страниц (минимально)

    Если нужно перенаправить пользователя (например, со старого адреса):

    replace заменяет запись в истории, чтобы «Назад» не возвращал на редирект.

    8) Частые ошибки

  • Путают Link to и a href: для внутренних страниц — Link/NavLink.
  • Забывают path="*": тогда неизвестные адреса показывают пустоту.
  • Делают layout без <Outlet />: вложенные страницы не появятся.
  • Ожидают число из useParams(): там всегда строка.
  • ---

    Задания для закрепления

  • Подключите React Router и сделайте маршруты / и /about.
  • Вынесите общий layout с Header и Footer и подключите его через вложенные маршруты.
  • Сделайте меню в Header на NavLink и добавьте активный класс.
  • Добавьте динамический маршрут /projects/:slug и выведите slug на странице.
  • Добавьте страницу 404 через path="*".
  • На странице списка добавьте фильтр через query‑параметр ?tag=... (например, кнопки all, react, ui).
  • <details> <summary> Ответы </summary>

    1) Установка:

    2) Базовые маршруты:

    3) Layout + Outlet:

    AppLayout должен содержать <Outlet />.

    4) Активные ссылки:

    5) Динамический маршрут и параметры:

    6) 404:

    7) Query‑параметры:

    </details>

    7. Данные и деплой: API, сборка и публикация

    Данные и деплой: API, сборка и публикация

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

    В этой статье разберём: как безопасно работать с API в React и что важно учесть при деплое SPA.

    1) Откуда брать данные для сайта

    На простом сайте чаще всего встречаются варианты:

  • Локальные данные (массив в файле) — удобно для прототипа.
  • API (HTTP-запросы) — когда данные должны обновляться без пересборки.
  • Смешанный вариант — часть контента локально, часть с API.
  • Почти всегда API отдаёт JSON. В браузере для запросов часто используют fetch.

    2) Базовый паттерн загрузки данных: loading/error/data

    Для запроса вам почти всегда нужны три состояния:

  • data — результат
  • isLoading — идёт ли загрузка
  • error — произошла ли ошибка
  • Хуки useState/useEffect уже встречались ранее (см. статью про состояние и события). Здесь применим их к загрузке.

    Пример страницы, которая загружает список проектов:

    А в компоненте останется только логика UI.

    4) Переменные окружения в Vite: разные API для dev/prod

    Частая схема:

  • локально API на одном адресе
  • в проде — на другом
  • В Vite переменные, доступные в коде, должны начинаться с VITE_.

    Пример:

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

    jsx useEffect(() => { async function load() { const res = await fetch("/api/projects") const json = await res.json() setProjects(json) } load() }, []) js export async function getProjects() { const res = await fetch("/api/projects") if (!res.ok) throw new Error(HTTP {base}/projects) ``

    5) Проверка деплоя SPA:

  • Если обновление /about даёт 404, нужно включить правило rewrite/fallback на index.html на стороне хостинга.
  • Если сайт опубликован в подпути и ломаются ассеты/роутинг, проверьте base в vite.config.js и basename у BrowserRouter`.
  • </details>