Первая программа на React
Play ITЗагрузка интерактивного демо…
Первая программа на React
Где применяют React
React — библиотека для пользовательского интерфейса. Вы описываете экран компонентами (функциями, которые возвращают разметку). Когда меняется состояние (число в счётчике, текст в поле), React пересчитывает, что показать, и обновляет DOM точечно — без полной перезагрузки страницы.
Маршруты вроде /about и HTTP-сервер подключают отдельно — React Router или Next.js. Сам React отвечает за экран — один компонент, один URL через Vite, через минуты виден результат.
В этой статье соберём:
- Счётчик и поле имени —
useState - Заголовок вкладки от счётчика —
useEffect - Список заметок с Node API —
fetch
Сравнение: Vue · Angular · Next.js. Склейка с API: Fullstack на JavaScript — API и фронтенд. Обзор: React - библиотека для пользовательских интерфейсов. Галерея готовых компонентов с разбором — React — компоненты-рецепты. Аналог для Vue и Svelte — галерея компонентов.
Что получится
| Часть | Результат |
|---|---|
| Проект | Vite + React на http://localhost:5173 |
| UI | Счётчик, ввод имени, список с сервера |
| API | GET http://127.0.0.1:3000/notes (если Node запущен) |
Создание проекта
create-react-app устарел для новых учебных проектов. Стандарт — Vite: быстрый dev-сервер и сборка.
npm create vite@latest my-first-app -- --template react
cd my-first-app
npm install
npm run dev
| Команда | Что делает |
|---|---|
npm create vite@latest | Скачивает шаблон проекта |
npm install | Ставит зависимости из package.json |
npm run dev | Поднимает сервер; при сохранении файла страница обновляется |
В браузере откройте адрес из консоли (обычно http://localhost:5173).
Компонент и useState
src/App.jsx — корневой компонент приложения:
Код ITЗагрузка примера кода…
Разбор:
useStateподключается изreactи создаёт локальное состояние компонента.const [count, setCount]иconst [name, setName]через деструктуризацию получают текущее значение и setter.value={name}+onChange=...делают поле ввода контролируемым компонентом.- Условие
{name && <h2>...рендерит приветствие только при непустом значении. - Обработчики
onClickпередают функции, поэтому обновление происходит по событию, а не во время рендера.
Разбор по строкам
| Код | Смысл |
|---|---|
useState(0) | React создаёт ячейку памяти: значение 0 и функцию обновления |
const [count, setCount] | Деструктуризация: count — текущее, setCount — "записать новое" |
setCount(count + 1) | После вызова React заново вызывает App с новым count |
value={name} | Input подчинён state: отображается то, что в React |
onChange={(e) => setName(e.target.value)} | При вводе читаем текст из DOM и кладём в state |
{name && <h2>…} | Если name пустой — блок не рисуется |
onClick={() => setCount(count + 1)} | В onClick передаём функцию. onClick={setCount(1)} вызовет её сразу при рендере — ошибка |
JSX — синтаксис "разметка внутри JavaScript". Vite (Babel) превращает <h1> в вызов React.createElement('h1', …).
Однонаправленный поток данных: state живёт в родителе; детям передают props и колбэки для событий.
useEffect — побочные эффекты
Рендер компонента должен быть предсказуемым: только "по state/props нарисовать UI". Всё, что трогает внешний мир — заголовок вкладки, fetch, таймеры — выносят в useEffect:
import { useState, useEffect } from 'react';
export default function App() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Счётчик: ${count}`;
}, [count]);
// ...
}
| Часть | Смысл |
|---|---|
| Первый аргумент | Функция-эффект — что выполнить |
Второй [count] | Зависимости: повторить эффект, когда изменился count |
[] | Пустой массив — эффект один раз после первого монтирования |
return () => { … } | Cleanup — перед размонтированием или перед следующим запуском |
Пример с таймером:
useEffect(() => {
const id = setInterval(() => setCount((c) => c + 1), 1000);
return () => clearInterval(id);
}, []);
Разбор:
setIntervalзапускает периодическое действие раз в 1000 мс.- Функциональная форма
setCount((c) => c + 1)использует актуальное предыдущее значение. return () => clearInterval(id)очищает таймер при размонтировании и предотвращает утечки.- Пустой массив зависимостей
[]означает запуск эффекта один раз после первого рендера.
setCount((c) => c + 1) — обновление от предыдущего значения.
Загрузка данных — fetch и Node API
Поднимите API заметок на порту 3000. В React (с CORS на сервере — Express — middleware, маршруты и ошибки):
Код ITЗагрузка примера кода…
Разбор:
notesхранит список,errorхранит сообщение ошибки для UI.useEffect(..., [])запускает загрузку данных только при первом монтировании компонента.- В
.then((res) => ...)проверяетсяres.ok, чтобы корректно обработать HTTP-статусы. .then(setNotes)передаёт в state готовый массив заметок из JSON.notes.map(...)рендерит элементы списка, аkey={n.id}помогает React корректно сопоставлять узлы.
| Строка | Смысл |
|---|---|
useState([]) | Начальный список пустой |
useEffect(..., []) | Загрузить один раз при появлении компонента |
.then(setNotes) | setNotes получит распарсенный массив |
key={n.id} | Стабильный ключ для списка между рендерами |
Галерея типовых запросов с построчным разбором — GET, POST, Bearer, таймаут и React useEffect: Fetch / axios — типовые запросы. Проверка API в терминале — curl / fetch — примеры.
Подключите в App.jsx: <NotesList /> под счётчиком.
Условный рендеринг и списки
Интерфейс почти всегда зависит от данных: показать загрузку, пустой список или карточки.
| Приём | Пример | Когда |
|---|---|---|
&& | {isLoading && <Spinner />} | Показать блок, если условие истинно |
| Тернарный | {error ? <p>{error}</p> : <List />} | Два варианта разметки |
.map() | {items.map((x) => <Row key={x.id} … />)} | Список однотипных элементов |
key — стабильный идентификатор элемента (id из API, лучше не индекс массива при удалении и сортировке). React по key сопоставляет старые и новые узлы и сохраняет фокус и внутреннее состояние строки.
{notes.length === 0 ? (
<p>Заметок пока нет</p>
) : (
<ul>
{notes.map((n) => (
<li key={n.id}>{n.text}</li>
))}
</ul>
)}
Подробнее — справочник React, обзор — React - библиотека для пользовательских интерфейсов.
React Router v6 — минимум
Один компонент App на весь сайт хватает для учебы. Для страниц /about, /notes подключают react-router-dom:
npm install react-router-dom
Код ITЗагрузка примера кода…
| Элемент | Назначение |
|---|---|
BrowserRouter | Связывает URL в адресной строке с деревом React |
Routes / Route | Какой компонент показать при каком path |
Link / NavLink | Навигация без полной перезагрузки страницы |
:id в path | Динамический сегмент; в компоненте — useParams() |
path="*" | Страница 404 для неизвестных URL |
Для SEO и серверного HTML чаще берут Next.js — там маршруты из структуры папок app/.
В vite.config.js можно настроить server.proxy: запрос /api/notes уходит на localhost:3000/notes. Подробнее: Fullstack 264.
Вынести счётчик в дочерний компонент
src/components/Counter.jsx:
export function Counter({ value, onIncrement, onDecrement, onReset }) {
return (
<section className="counter">
<h2>Счётчик: {value}</h2>
<button type="button" onClick={onDecrement}>−</button>
<button type="button" onClick={onReset}>Сброс</button>
<button type="button" onClick={onIncrement}>+</button>
</section>
);
}
Разбор:
- Компонент получает всё через props и не хранит собственный
count. valueотвечает только за отображение текущего состояния на экране.- Колбэки
onIncrement,onDecrement,onResetделегируют изменение состояния родителю. - Такой контракт делает
Counterпереиспользуемым и простым для тестирования.
В App.jsx state остаётся в родителе — подъём состояния (lifting state up):
<Counter
value={count}
onIncrement={() => setCount(count + 1)}
onDecrement={() => setCount(count - 1)}
onReset={() => setCount(0)}
/>
Разбор:
- Родитель остаётся владельцем
count, поэтому поток данных идёт сверху вниз. value={count}передаёт дочернему компоненту текущее значение без мутаций.- Функции в
onIncrement/onDecrement/onResetописывают разрешённые действия над состоянием. - Этот паттерн называется lifting state up и помогает синхронизировать несколько дочерних компонентов.
Композиция главной страницы из секций
Если нужно показать архитектуру лендинга (а не только счётчик), удобно вынести каждый блок в отдельный компонент и собрать страницу в корневом App.jsx:
Код ITЗагрузка примера кода…
Разбор:
- Блок
importзадаёт композицию страницы из независимых секций. Appвыступает оркестратором: собирает layout и порядок отображения контента.- Фрагмент
<>...</>группирует несколько корневых узлов без лишнего DOM-элемента. - Разделение на секции снижает связность и упрощает параллельную работу команды.
Такой подход упрощает поддержку: одна секция — один файл и одна зона ответственности.
Классовые компоненты
До появления хуков писали class App extends Component и this.setState. В новых проектах используют функции и хуки; классы остаются в legacy-коде — см. справочник React.
Частые ошибки
| Симптом | Причина | Что сделать |
|---|---|---|
Too many re-renders | setCount(...) в теле компонента | Вызывать setter только в обработчике или useEffect |
| Кнопка срабатывает при загрузке | onClick={handle()} | Передать функцию: onClick={() => handle()} |
| Список сбивается при удалении | Нет key или key={index} при перестановке | Стабильный key из данных (id) |
Бесконечный цикл в useEffect | В зависимостях объект/массив, создаваемый заново каждый рендер | Уточнить deps или мемоизировать значение |
fetch failed / CORS | API выключен или нет CORS | Fullstack на JavaScript — API и фронтенд, прокси в Vite |
| State не меняется после setState | Мутация массива/объекта | Новая ссылка: setItems([...items, newItem]) |
| Прыгает фокус в форме | Прямая работа с DOM вместо state | Контролируемый input + value / onChange |
Полный список — справочник.
Что попробовать
- POST-заметку:
fetchсmethod: 'POST'иJSON.stringify({ text })— с блокировкой кнопки на время запроса: Кнопка с загрузкой — React, Promise и поток обновлений. - Next.js 2731 — тот же UI в
app/counter/page.tsxс'use client'. - Electron 118 — React в окне десктопа.
Мини-проект для закрепления React
После базового счётчика лучше сразу сделать маленький законченный сценарий. Он связывает форму, список, API и состояние в одном упражнении.
Идеи мини-проектов
| Уровень | Проект | Что тренируете |
|---|---|---|
| Начальный | Todo (список дел) | useState, .map(), key, фильтр все / активные |
| Начальный | Калькулятор | Несколько полей state, обработчики кнопок |
| Начальный | Заметки / блокнот | Форма + список, локально или через API |
| Начальный | Форма входа | Валидация, controlled inputs, сообщения об ошибке |
| Средний | Погода / Movie Search | useEffect, fetch, loading и error |
| Средний | Тёмная / светлая тема | Context или useState + localStorage |
| Средний | Поиск с фильтром | Фильтрация массива в рендере, debounce ввода |
| Средний | Пагинация по API | Страницы, query-параметры, кнопки "назад / вперёд" |
| Средний | Адаптивная шапка | Router + условные классы, NavLink |
Главное задание раздела — проект "Заметки" ниже: форма, CRUD и работа с API.
Задание "Заметки"
- Поле ввода
textи кнопка "Добавить". - Список заметок из API.
- Кнопка удаления каждой заметки.
- Индикатор загрузки и обработка ошибок.
Архитектура компонентов
App
NoteForm
NotesList
NoteItem
Разбор:
- Это дерево показывает иерархию компонентов и направление передачи props.
Appхранит общее состояние и бизнес-операции.NoteFormотвечает за ввод и отправку данных.NotesListрендерит коллекцию, аNoteItemинкапсулирует отображение/действия для одной заметки.
Что проверить вручную
- Пустой текст не отправляется.
- После успешного POST список обновляется без перезагрузки страницы.
- После DELETE карточка исчезает и интерфейс остаётся консистентным.
- При выключенном API показывается понятная ошибка.
Связанные материалы
| Тема | Материал |
|---|---|
| Галерея компонентов | React — компоненты-рецепты |
| Кнопка с загрузкой | Кнопка с загрузкой — React, Promise и поток обновлений |
| Обзор React | React - библиотека для пользовательских интерфейсов |
| Node API | Первая программа на Node.js · Express — middleware, маршруты и ошибки |
| Fullstack | Fullstack на JavaScript — API и фронтенд |
| TypeScript + React | TypeScript |
| Мобильный UI (Dart) | Flutter · виджеты (Lab) |
| Тесты компонентов | Тестирование JavaScript — Vitest и Testing Library |
Базовый разбор HTTP и HTTPS находится в отдельной статье — HTTP как основа веб-интеграций.
В подборках
Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:
Первые шаги (маршрут подборки) — Первая программа на visual-basic, Первая программа на Vue.js, Первая программа на Pascal, Первая программа на Angular, Первая программа на Lisp, Первая программа на Ext JS.