React — компоненты-рецепты
Приветствую! Здесь вы наверняка найдете, что ищете. Примеры в лаборатории рассчитаны на то, что мы разбираем что-то конкретное.
Текущая статья посвящена счётчик useState, todo list, форма, modal, fetch, Router. Готовый код для лабораторной, курсовой, react counter example и react todo app.
Поэтому за теорией по текущей теме вам — в энциклопедию. Если ещё не погружались, то маршрут прост:
- Основы
- Система и сеть
- Данные и разметка
- Код и разработка
- Языки
- Искусственный интеллект
- Проект
- Инфраструктура и безопасность
- Спин-офф
Обязательно пройдитесь.
А теперь приступим к нашему предмету.
Обзор React — библиотека для пользовательских интерфейсов.
Пошаговый старт — Первая программа на React.
Справочник API — Справочник по React.
HTTP-запросы — Fetch / axios и curl / fetch.
Стили — HTML + CSS — макеты, Tailwind — готовые блоки, UI-паттерны Uiverse (Galaxy), типовые элементы CSS.
Типы для props — TypeScript и React.
Десктоп на Python — Tkinter — окна и виджеты.
Мобильный UI на Dart — Flutter и Flutter — готовые виджеты.
- Создайте проект Vite + React — команды в каркасе.
- Скопируйте весь блок кода примера в
src/App.jsx(или вынесите вsrc/components/). - Запустите
npm run dev, откройте адрес из консоли (обычноhttp://localhost:5173). - Прочитайте Разбор и таблицу под кодом — там смысл строк и типичные ошибки.
- Измените текст, цвета, начальные данные — так быстрее запоминается API.
Навигация по примерам
| Раздел | Тема |
|---|---|
| Каркас проекта | vite react template, npm create vite react, react hello world |
| Кнопка с вариантами | react button component props, button variant primary outline |
| Skeleton-карточка | react skeleton loading, react loading placeholder card |
| Switch темы | react dark mode toggle, localstorage theme react |
| Tooltip | react tooltip component, aria-describedby tooltip |
| Те же UI на TypeScript | react typescript button props, react-ts vite template |
| Счётчик | react usestate counter, react onclick button, increment counter react |
| Приветствие | react controlled input, react onchange input, react form name |
| Переключатель | react toggle button, react boolean usestate |
| Настройки | react checkbox example, react radio button, controlled checkbox react |
| Todo | react todo list, react map key, todo app react hooks |
| Конвертер | react number input, react calculator example, temperature converter react |
| Модальное окно | react modal component, react dialog popup, conditional render modal |
| Вкладки | react tabs component, active tab state react |
| Поиск по списку | react filter list, react search input filter array |
| Форма входа | react login form, react form validation, react preventdefault submit |
| Загрузка и ошибка | react loading spinner, react conditional render loading |
| Список с API | react fetch useeffect, react get data from api, react useeffect empty array |
| Подъём state | react lifting state up, react props callback parent |
| Свой хук | react custom hook, uselocalstorage react hook |
| Тема Context | react context example, react dark mode context |
| Router | react router v6 example, react routes link, react spa navigation |
| Лендинг | react component composition, react landing page sections |
Словарь React за 30 секунд
| Понятие | Зачем | Как в коде |
|---|---|---|
| Компонент | Кусок UI — функция, возвращающая JSX | function App() { return <div>…</div> } |
| JSX | «HTML внутри JavaScript» | <button onClick={…}>Текст</button> |
| Props | Входные данные от родителя | function Btn({ label, onClick }) |
| State | Данные, меняющиеся со временем | const [n, setN] = useState(0) |
| Setter | Записать новое значение state | setN(n + 1) или setN((x) => x + 1) |
| Controlled input | Поле подчинено state | value={text} + onChange={…} |
| key | ID элемента в списке | {items.map((x) => <li key={x.id}>… |
| useEffect | Побочный эффект после рендера | fetch, таймер, document.title |
| children | Содержимое между тегами компонента | <Modal>…текст…</Modal> |
Односторонний поток данных: state хранит владелец (обычно родитель). Детям передают props и колбэки (onSave, onClick). Менять props внутри ребёнка нельзя — только вызвать переданную функцию.
Как работает React — цикл обновления
Браузер не перезагружает всю страницу при каждом клике. React пересчитывает, что изменилось, и обновляет DOM точечно.
flowchart LR
A[Клик / ввод текста] --> B[setState / setter]
B --> C[React вызывает компонент заново]
C --> D[Сравнение Virtual DOM]
D --> E[Обновление только изменившихся узлов]
Пользователь нажал «+» → setCount → React снова вызвал App с новым count → на экране обновилась только цифра в заголовке.
Обязательный каркас
Любой пример ниже опирается на проект Vite + React. Запомните команды — как import tkinter и mainloop() в Tkinter.
Задача: поднять пустой React-проект и убедиться, что dev-сервер работает.
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
npm run dev
Для рецептов Button / Skeleton / Switch / Tooltip на TypeScript:
npm create vite@latest my-react-ts -- --template react-ts
cd my-react-ts
npm install
npm run dev
| Команда | Что делает |
|---|---|
npm create vite@latest |
Скачивает шаблон проекта (быстрее старого create-react-app) |
npm install |
Ставит зависимости из package.json |
npm run dev |
Поднимает сервер; при сохранении файла страница обновляется |
Минимальный src/App.jsx:
import './App.css';
export default function App() {
return (
<div className="app">
<h1>Моё React-приложение</h1>
</div>
);
}
Разбор:
| Фрагмент | Смысл |
|---|---|
export default function App |
Корневой компонент — точка входа UI; Vite подключает его в main.jsx |
className="app" |
CSS-класс. В JSX пишут className, потому что class — зарезервированное слово в JavaScript |
return ( … ) |
JSX — результат функции; React рисует это в DOM |
| `` | Комментарий внутри JSX |
Типичные ошибки при старте:
- Забыли
npm install— командаnpm run devпадает с «module not found». - Редактируете не тот файл — рабочий код обычно в
src/App.jsx, не вindex.html. - Порт занят — Vite предложит другой адрес в консоли, откройте его.
Попробуйте: добавьте import { useState } from 'react' и перейдите к счётчику.
Стартовые компоненты
Простые блоки «с нуля» — с них удобно начинать лабораторную или первый commit в GitHub.
Счётчик
Задача: показать, что React хранит число в памяти и обновляет экран по клику — минимум для проверки установки. Классический react counter example.
Разбор:
import { useState } from 'react'— хук для локального state в функциональном компоненте.useState(0)создаёт пару: текущее значениеcountи функциюsetCountдля записи нового.{count}в JSX — вставка JavaScript-выражения в разметку; при измененииcountReact перерисует только этот текст.onClick={() => setCount(count + 1)}— в обработчик передают функцию. Если написатьonClick={setCount(count + 1)}, setter вызовется сразу при рендере — типичная ошибка новичка.type="button"— кнопка внутри<form>случайно не отправит форму.
| Строка | Смысл |
|---|---|
useState(0) |
Начальное значение счётчика — ноль |
setCount(0) |
Явный сброс |
setCount((c) => c + 1) |
Безопаснее при частых кликах — берёт предыдущее значение |
Попробуйте: замените setCount(count + 1) на setCount((c) => c + 1). Добавьте useEffect, меняющий document.title — см. 272.
Поле имени и приветствие
Задача: прочитать текст из поля и показать результат на экране — типичная форма «введите имя». Основа controlled input в React.
Разбор:
value={name}делает input контролируемым: на экране только то, что лежит в state React.onChange={(e) => setName(e.target.value)}— при каждом символе читаем текст из DOM (e.target.value) и кладём в state.{name.trim() && <h2>…}— условный рендеринг: блок<h2>рисуется, только если послеtrim()имя не пустое.trim()убирает пробелы по краям — иначе один пробел уже покажет «Привет».
| Без state | С state (React) |
|---|---|
Браузер сам хранит текст в <input> |
React хранит текст в name, input его отображает |
Читают через ref или DOM |
Читают через name в коде — удобно для валидации и отправки |
Попробуйте: кнопка «Очистить» — onClick={() => setName('')}. Поле «Фамилия» — второй useState.
Переключатель вкл/выкл
Задача: boolean state и смена подписи по клику — основа переключателей, «лайков», видимости блоков.
Разбор:
useState(false)— начальное значение «выключено».setOn((v) => !v)переключает boolean от предыдущего значения — надёжнее, чемsetOn(!on)при сложной логике.- Тернарный оператор
on ? '…' : '…'в JSX выбирает текст кнопки и статуса.
Попробуйте: добавьте className={on ? 'active' : ''} на контейнер и стиль в App.css.
Флажки и переключатели роли
Задача: несколько настроек «вкл/выкл» и выбор одного варианта из списка (роль, режим, тариф).
Разбор:
checked={notify}+onChangeсe.target.checked— контролируемый checkbox (галочка синхронизирована со state).- У radio одно имя
name="role", разныеvalue; в state хранится выбранная строка'user'или'admin'. .filter(Boolean)убираетfalseиз массива — остаются только включённые опции для строки статуса.
Попробуйте: добавьте третий checkbox «Тёмная тема» и выводите его в enabled.
Компоненты-рецепты
Типовые блоки интерфейса для лабораторных, портфолио и pet-проектов. Каждый можно вынести в src/components/Имя.jsx.
Стили ниже опираются на типовые элементы CSS и каталог Uiverse / Galaxy: сначала вёрстка в CSS, затем обёртка в React с props и state.
Кнопка с вариантами (Button)
Задача: одна переиспользуемая кнопка с вариантами primary / outline / danger и состоянием загрузки — как в design system, по мотивам кнопок в 113.
src/components/Button.jsx:
Разбор:
| Строка | Смысл |
|---|---|
VARIANT_CLASS |
Словарь «вариант → className» — без длинных if в JSX |
...rest |
Пробрасываем onClick, className, aria-* с проверкой в IDE при TS |
aria-busy |
Скринридер понимает, что кнопка в процессе |
loading || undefined |
Атрибут снимается, когда загрузки нет |
Полный набор на .tsx — ниже, TypeScript.
Попробуйте: size="sm" — третий ключ в словаре и модификатор .button--sm.
Skeleton-карточка и загрузка данных
Задача: пока fetch не завершился, показать skeleton; после ответа — карточку с текстом.
src/components/CardSkeleton.jsx:
export function CardSkeleton() {
return (
<article className="card-skeleton" aria-busy="true" aria-label="Загрузка карточки">
<div className="card-skeleton__line card-skeleton__line--title" />
<div className="card-skeleton__line" />
<div className="card-skeleton__line card-skeleton__line--short" />
</article>
);
}
src/components/ProfileCard.jsx:
src/App.jsx:
import { ProfileCard } from './components/ProfileCard';
export default function App() {
return (
<div className="app">
<h1>Профиль</h1>
</div>
);
}
Стили shimmer — Типовые элементы интерфейса или CSS-анимации — готовые эффекты — Skeleton.
Разбор:
CardSkeleton— презентационный компонент без state; переиспользуется везде, где ждём API.status === 'loading'— тот же приём, что в загрузке и ошибке; вместо<p>Загрузка…</p>— skeleton.aria-busyна skeleton снимается автоматически, когда компонент размонтирован послеok.
Попробуйте: замените setTimeout на fetch; при ошибке покажите <p className="error"> вместо skeleton.
Switch темы и localStorage
Задача: переключатель как в Galaxy / Uiverse — визуально switch, выбор сохраняется после перезагрузки (когда нужен JS).
src/components/ThemeSwitch.jsx:
src/App.css:
:root { --bg: #f8f9fa; --fg: #212529; }
html.theme-dark { --bg: #1a1a2e; --fg: #eee; }
body { background: var(--bg); color: var(--fg); transition: background 0.3s, color 0.3s; }
/* стили .switch / .switch-track — из encyclopedia 3.10.113 */
Разбор:
| Часть | Смысл |
|---|---|
useState(() => …) |
Ленивый старт: читаем localStorage один раз при монтировании |
useEffect + document.documentElement |
Тема на корне документа — все страницы наследуют CSS-переменные |
role="switch" + checked |
Контролируемый input — React — источник правды |
aria-labelledby |
Подпись switch для доступности |
Глобальная тема для всего дерева без prop drilling — Context; здесь — минимальный вариант для одной страницы.
Попробуйте: синхронизировать с prefers-color-scheme при первом визите.
Tooltip (подсказка)
Задача: обёртка с подсказкой по hover и фокусу с клавиатуры — CSS-only из UI-паттерны из Uiverse (Galaxy) недостаточен для touch; в React управляем видимостью явно.
src/components/Tooltip.jsx:
Разбор:
useId()— уникальныйidдляaria-describedbyбез коллизий при нескольких tooltip на странице.onFocus/onBlurна обёртке — подсказка доступна с Tab, не только с мыши.children— любой элемент (кнопка, иконка, ссылка); паттерн composition.
Попробуйте: закрывать tooltip по Escape — onKeyDown на обёртке.
Те же UI-компоненты на TypeScript (.tsx)
Задача: перенести Button, Skeleton, ThemeSwitch и Tooltip в проект Vite + React + TypeScript — типы ловят опечатки в variant и полях профиля до запуска. Теория — TypeScript и React, типизация UI.
Каркас:
npm create vite@latest my-ui-ts -- --template react-ts
cd my-ui-ts
npm install
npm run dev
Структура файлов (стили App.css — те же, что в JSX-рецептах выше):
src/
├── components/
│ ├── Button.tsx
│ ├── CardSkeleton.tsx
│ ├── ProfileCard.tsx
│ ├── ThemeSwitch.tsx
│ └── Tooltip.tsx
├── types/
│ └── ui.ts
├── App.tsx
└── main.tsx
src/types/ui.ts — общие типы UI:
export type ButtonVariant = 'primary' | 'outline' | 'danger';
export type Profile = {
name: string;
role: string;
};
export type ProfileLoadState =
| { status: 'loading' }
| { status: 'ok'; profile: Profile }
| { status: 'error'; message: string };
src/components/Button.tsx:
Разбор TypeScript:
| Конструкция | Зачем |
|---|---|
ButtonVariant union |
variant="primry" — ошибка компиляции, не опечатка в runtime |
Record<ButtonVariant, string> |
В словаре CSS обязаны быть все варианты |
ProfileLoadState discriminated union |
В ветке status === 'ok' TypeScript знает поле profile |
ChangeEvent<HTMLInputElement> |
Тип события checkbox/switch |
TooltipProps + ReactElement |
В tooltip — один дочерний элемент (кнопка), не произвольный текст |
import type { … } |
Типы не попадают в JS-бандл |
Проверка типов без запуска браузера:
npx tsc --noEmit
В шаблоне react-ts сборка обычно уже включает проверку: npm run build.
Типичные ошибки:
Ошибка IDE / tsc |
Причина | Исправление |
|---|---|---|
Property 'profile' does not exist |
Обращение к profile без проверки status |
Сузить union: if (state.status !== 'ok') return … |
Type 'string' is not assignable to ButtonVariant |
Динамическая строка в variant |
Приведите тип или проверьте значение guard-ом |
Tooltip: children must be ReactElement |
Передали текст напрямую | Оборачивайте в <Button>…</Button> |
Попробуйте: вынесите ButtonProps в отдельный файл и экспортируйте из components/Button.tsx для Storybook. Добавьте size?: 'sm' | 'md' в ButtonProps и класс .button--sm.
Todo-список с фильтром
Задача: добавление задач, отметка «готово», фильтр «все / активные / выполненные» — react todo list, классика для GitHub-портфолио.
Разбор:
| Строка / конструкция | Смысл |
|---|---|
e.preventDefault() |
Форма не перезагружает страницу при Enter или клике «Добавить» |
setItems((prev) => [..prev, newItem]) |
Новый массив — React видит изменение. prev.push(x) сломает обновление |
{ ..item, done: !item.done } |
Копия объекта с одним изменённым полем — без мутации |
key={item.id} |
Стабильный ключ для .map(). React сопоставляет строки между рендерами |
Date.now() как id |
Подходит для учебного проекта; в проде — id с сервера |
filterв state и.filter()на массиве — два разных «filter»: первый — режим UI, второй — метод массива.className={item.done ? 'done' : ''}— зачёркивание через CSS (.done { text-decoration: line-through; }).
Попробуйте: кнопка «Удалить выполненные» — setItems(items.filter((x) => !x.done)). Счётчик «Осталось: N» — items.filter((x) => !x.done).length.
Конвертер °C → °F
Задача: ввод числа, формула, вывод результата — частое задание на информатике (аналог конвертера в Tkinter).
Разбор:
replace(',', '.')— пользователь может ввести25,5с русской раскладкой.Number.isNaN(celsius)— проверка после преобразования; пустая строка дастNaN.- Формула: $F = C \times \frac{9}{5} + 32$.
onKeyDown+Enter— перевод по Enter без отдельной кнопки (удобно на формах).
Попробуйте: кнопка «Очистить» — setRaw(''), setResult('—'), setError('').
Модальное окно
Задача: показать диалог поверх страницы без библиотек — react modal component с нуля.
Попробуйте: второе модальное окно «Ошибка» с другим title и тем же компонентом Modal.
Вкладки
Задача: переключение блоков контента без перезагрузки — аналог вкладок в браузере или настройках приложения.
Разбор:
- Массив
TABSхранит id, подпись кнопки и текст вкладки — удобно расширять без копипасты JSX. active— id текущей вкладки; один state управляет и кнопками, и содержимым.TABS.find((t) => t.id === active)— объект текущей вкладки для выводаbody.current?.body— optional chaining: если id не найден, ошибки нет.aria-selected— подсказка assistive tech, какая вкладка активна.
Попробуйте: добавьте четвёртую вкладку с JSX вместо строки — например, <ul><li>Пункт</li></ul> в поле body (тогда body станет React-узлом, не строкой).
Поиск по списку
Задача: фильтрация массива по подстроке в реальном времени — основа поиска в таблицах, каталогах, автодополнении.
Разбор:
toLowerCase()на обеих сторонах — поиск без учёта регистра (москнайдёт «Москва»)..includes(q)— подстрока где угодно в названии.- Пустой
query— показываем весь списокCITIES. key={city}допустим, потому что названия уникальны; при дубликатах нужен отдельныйid.
Попробуйте: подсветка совпадения через <mark>. Для длинных списков — useMemo (см. справочник).
Форма входа с проверкой
Задача: email, пароль, сообщения об ошибках — типичный экран react login form для учебного проекта.
Разбор:
| Часть | Смысл |
|---|---|
onSubmit={onSubmit} |
Отправка формы перехватывается JavaScript |
e.preventDefault() |
Страница не перезагружается (иначе state пропадёт) |
noValidate |
Отключаем встроенную HTML-валидацию — показываем свои сообщения |
Объект errors |
Ключ = поле, значение = текст ошибки; пустой объект = всё OK |
type="password" |
Символы скрыты; в учебном примере пароль только в state, без отправки на сервер |
Попробуйте: блокировать кнопку «Войти», пока поля пустые — disabled={!email || !password}.
Загрузка, ошибка, пустой список
Задача: три состояния UI при асинхронной работе — loading, error, data. Шаблон для любого fetch.
Разбор:
- Ранний
returnдля каждого состояния — читаемый условный рендеринг без вложенных тернарников. useEffect(.., [])— эффект один раз после первого появления компонента на экране.return () => clearTimeout(timer)— cleanup: если компонент исчезнет до срабатывания таймера, таймер отменится (нет утечки).- В реальном проекте вместо
setTimeout—fetch; логика состояний та же.
Попробуйте: замените таймер на fetch('https://jsonplaceholder.typicode.com/users') и .then((r) => r.json()).
Список заметок с API
Задача: загрузить JSON с сервера и отрисовать список — связка useEffect + fetch, как в 272.
Разбор:
| Строка | Смысл |
|---|---|
useState([]) |
Пока данных нет — пустой массив |
useEffect(.., []) |
Запрос один раз при монтировании; без [] — бесконечные запросы |
if (!res.ok) |
HTTP 404/500 — ошибка до парсинга JSON |
.then(setNotes) |
setNotes получит готовый массив из .json() |
.finally(() => setLoading(false)) |
Спиннер скрывается и при успехе, и при ошибке |
key={n.id} |
Стабильный ключ из данных API |
Подключите в App.jsx: `` под другими блоками.
CORS и прокси Vite — Fullstack на JavaScript — API и фронтенд. Node API — Первая программа на Node.js. Готовые шаблоны fetch — Fetch / axios — типовые запросы.
Попробуйте: POST-заметку через fetch с method: 'POST' и JSON.stringify({ text }).
Подъём state — переиспользуемый Counter
Задача: вынести кнопки в отдельный компонент, а число хранить в родителе — паттерн lifting state up.
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>
);
}
src/App.jsx:
Разбор:
Counterне вызываетuseState— только отображает props и вызывает колбэки.- Родитель владеет
count— один источник правды для всего дерева. value={count}— данные вниз (props).onIncrement={() => …}— события вверх (колбэки).- Так один state можно разделить между несколькими детьми (например, счётчик и график).
Попробуйте: второй Counter с тем же count — оба синхронизированы автоматически.
Свой хук useLocalStorage
Задача: сохранить значение между перезагрузками вкладки (F5) — типичный custom hook.
src/hooks/useLocalStorage.js:
Использование в App.jsx:
import { useLocalStorage } from './hooks/useLocalStorage';
export default function App() {
const [name, setName] = useLocalStorage('username', '');
return (
<input
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Имя сохранится после F5"
/>
);
}
Разбор:
- Имя хука обязательно начинается с
use— правило React для хуков. useState(() => …)— ленивая инициализация: чтениеlocalStorageодин раз при первом рендере.useEffectзаписывает в storage при каждом измененииvalue.JSON.stringify/parse— работает со строками и числами; для сложных объектов следите за форматом.try/catch— если в storage мусор, вернётсяinitial.
Попробуйте: сохранить тему 'light' | 'dark' тем же хуком.
Тёмная тема через Context
Задача: передать настройку темы глубоко в дерево без «проброса» props через каждый уровень.
src/theme/ThemeContext.jsx:
Разбор:
createContext— «канал» для данных.Provider value={…}— все потомки могут прочитать value черезuseContext.useMemoдля объектаvalue— меньше лишних перерисовок дочерних компонентов.- шаблон
classNameс префиксомtheme-— переключение CSS-класса на корне (класс.theme-darkзадаёт фон и цвет текста).
Попробуйте: обернуть только часть страницы — увидите, что useTheme снаружи Provider выбросит ошибку.
React Router — минимальная навигация
Задача: несколько «страниц» в одностраничном приложении (SPA) без полной перезагрузки документа.
npm install react-router-dom
Разбор:
| Элемент | Назначение |
|---|---|
BrowserRouter |
Связывает URL в адресной строке с деревом React |
Link to="/about" |
Переход без перезагрузки HTML-документа |
NavLink |
Как Link, плюс класс active для текущего маршрута |
Routes / Route |
Какой компонент показать при каком path |
path="*" |
Fallback — страница 404 для неизвестных адресов |
Подробнее — 272 — Router. Для SEO и серверного HTML — Next.js.
Попробуйте: маршрут /users/:id и useParams() в компоненте профиля.
Лендинг из секций
Задача: показать архитектуру страницы — каждый блок в своём файле, App только собирает порядок.
Разбор:
- Фрагмент
<>…</>группирует несколько корневых узлов без лишнего<div>. App— компоновщик: задаёт layout и порядок секций.- Каждая секция — отдельный файл → параллельная работа в команде, удобнее сопровождать код.
- HTML-разметку секций можно взять из HTML + CSS — макеты или Tailwind — готовые блоки и перенести в JSX (заменить
classнаclassName, закрыть все теги).
Попробуйте: вынесите Hero в отдельный файл с props title и subtitle.
Переиспользуемые базы
Шаблон компонента с props по умолчанию
export function Card({ title = 'Без названия', children }) {
return (
<article className="card">
<h3>{title}</h3>
<div>{children}</div>
</article>
);
}
Разбор: title = '…' в деструктуризации — значение по умолчанию, если prop не передали. children — всё между <Card>…</Card>.
Сброс формы
function resetForm(setEmail, setPassword, setErrors) {
setEmail('');
setPassword('');
setErrors({});
}
Вызывайте после успешной отправки или по кнопке «Очистить».
Частые ошибки
| Симптом | Причина | Что сделать |
|---|---|---|
Too many re-renders |
setCount(1) или setCount(count+1) в теле компонента |
Setter только в обработчике или useEffect |
| Кнопка срабатывает при загрузке | onClick={handle()} со скобками |
onClick={handle} или onClick={() => handle()} |
| Input «не печатает» | Есть value, нет onChange |
Добавить controlled-пару |
| Список «ломается» при удалении | key={index} |
Стабильный id из данных |
Бесконечный useEffect |
Объект/массив в deps создаётся заново каждый рендер | Уточнить deps или useMemo |
fetch failed / CORS |
API выключен или нет заголовков CORS | Fullstack на JavaScript — API и фронтенд, прокси в Vite |
| State «не обновился» | Мутация: arr.push(x), obj.field = 1 |
Новая ссылка: [..arr, x], { ..obj, field: 1 } |
| Белый экран после правки | Синтаксическая ошибка JSX | Смотрите красный текст в терминале npm run dev |
Полный список — справочник React.
Что попробовать дальше
| Уровень | Задание | Материал |
|---|---|---|
| Начальный | Todo с удалением и фильтром «активные» | блок Todo |
| Начальный | Заметки с POST и DELETE | 272 — задание «Заметки» |
| Начальный | Калькулятор на несколько кнопок | счётчик + несколько useState |
| Средний | Погода или поиск фильмов по API | useEffect + Fetch / axios — типовые запросы |
| Средний | Десктоп на Electron | Первая программа Electron с React |
| Средний | TypeScript + типы props | TypeScript — TypeScript и React, TypeScript и React — типизация UI |
| Средний | UI из Galaxy → React | UI-паттерны из Uiverse (Galaxy), рецепты Button, Skeleton |
| Экзамен | 200 вопросов для самопроверки | 200 вопросов по React |
Связанные материалы
- React — обзор — карта тем и Virtual DOM
- Первая программа на React — Vite, хуки, Router, задание «Заметки»
- Справочник по React — API, HOC, тесты
- Fetch / axios — типовые запросы — шаблоны HTTP рядом с React
- Vue и Svelte — готовые компоненты — те же задачи на Vue/Svelte
- 200 вопросов по React — самопроверка перед зачётом
- Tkinter — окна и виджеты — те же задачи на Python
- Java Swing — окна и кнопки — те же задачи на Java
- HTML + CSS — готовые макеты — вёрстка секций для лендинга
- Tailwind — готовые блоки — hero, pricing, navbar на utility-классах
- UI-паттерны из Uiverse (Galaxy) — CSS-сниппеты и техники
- Типовые элементы интерфейса — кнопки, skeleton, switch, tooltip
- TypeScript и React — типизация UI