Гид по технологиям

Создание простого счётчика на React — пошаговое руководство

6 min read React Обновлено 04 Jan 2026
React счётчик — пошаговый туториал
React счётчик — пошаговый туториал

Аналоговые часы, стрелки на 12 часов, размытые минуты

React — одна из самых популярных библиотек JavaScript для frontend. Многие компании используют React для интерфейсов, а разработчики ценят его компонентный подход и удобные хуки. Простое приложение — счётчик — отлично подходит для знакомства с базовыми понятиями React и их практическим применением.

Что вы создадите

В этом проекте вы реализуете приложение-счётчик с тремя основными функциями:

  1. Увеличить счёт — прибавляет 1 к текущему значению.
  2. Уменьшить счёт — отнимает 1 от текущего значения.
  3. Сброс — устанавливает значение в 0.

Кроме того, мы разберём лучшие практики, альтернативные реализации, тесты, доступность и распространённые ошибки.

Основные понятия React (коротко)

  • Компоненты: переиспользуемые блоки UI. Компонент — это функция или класс, возвращающая JSX.
  • Состояние (state): данные компонента, которые влияют на рендер. В функциональных компонентах управляется хуками.
  • Функциональные компоненты: обычные JavaScript-функции, принимающие props и возвращающие JSX.
  • Props: свойства, передаваемые от родителя к ребёнку.
  • Хуки: функции, например useState и useReducer, которые дают доступ к состоянию и побочным эффектам внутри функциональных компонентов.

Короткая 1‑строчная дефиниция: useState — хук для локального состояния; useReducer — хук для сложной логики обновления состояния.

Быстрый старт — создание проекта (Step 1)

Откройте терминал и выполните:

npx create-react-app react-counter-app

Затем запустите сервер разработки:

npm start

По умолчанию приложение будет доступно на http://localhost:3000 и автоматически обновляется при изменениях.

Создаём каркас приложения (Step 2)

Откройте src/App.js и замените содержимое простым скелетом:

import React, { useState } from "react";  
  
function App() {  
  const [count, setCount] = useState(0);  
  let incrementCount = () => {  
  // To add later  
  };  
  let decrementCount = () => {  
  // To add later  
  };  
  let resetCount = () => {  
  // To add later  
  }  
  
return (  
  

Count: {count}

); } export default App;

Пояснение: useState создаёт локальное состояние count и функцию setCount для его обновления. Скобки {count} в JSX означают вставку JavaScript-значения.

Добавляем функциональность и кнопку-компонент (Step 3)

Добавьте три кнопки внутри

:

Теперь обновим функции, чтобы счётчик работал:

let incrementCount = () => {  
  setCount(count + 1);  
};  
  
let decrementCount = () => {  
  setCount(count - 1);  
};  
  
let resetCount = () => {  
  setCount(0);  
}

Создайте папку src/components и файл src/components/Button.js со следующим кодом:

import React from "react";  
  
function Button(props) {  
    let { action, title } = props;  
    return ;  
}  
  
export default Button;

Важно: при импорте компонента в App.js используйте корректный путь и имя файла:

import Button from "./components/Button";

В исходном примере была опечатка (“Botton”). Такая ошибка вызывает импортную ошибку во время сборки — исправьте имя файла/пути.

Ниже — финальная рабочая версия App.js (корректный импорт):

import React, { useState } from "react";  
import Button from "./components/Button";  
  
function App() {  
  const [count, setCount] = useState(0);  
  
  let incrementCount = () => {  
    setCount(count + 1);  
  };  
  
  let decrementCount = () => {  
    setCount(count - 1);  
  };  
  
  let resetCount = () => {  
    setCount(0);  
  }  
  
  return (  
    

Count: {count}

); } export default App;

Улучшения и лучшие практики

Ниже — набор практик и улучшений, применимых к этому простому приложению.

1) Используйте функциональное обновление setState там, где это важно

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

setCount(prev => prev + 1);
setCount(prev => prev - 1);

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

2) aria-атрибуты и доступность (a11y)

  • Кнопки должны иметь понятные метки (title/aria-label) и порядок фокуса.
  • Для визуального счёта добавьте role и aria-live, чтобы ассистивные технологии знали о динамике:

Count: {count}

3) Использование useReducer для более сложной логики

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

import React, { useReducer } from "react";

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return { count: 0 };
    default:
      throw new Error();
  }
}

function App() {
  const [state, dispatch] = useReducer(reducer, { count: 0 });
  return (
    

Count: {state.count}

); }

useReducer полезен также для предсказуемости и тестирования.

4) TypeScript‑вариант

Если проект на TypeScript, типизация props и стейта повышает надёжность:

type ButtonProps = {
  title: string;
  action: () => void;
}

function Button({ title, action }: ButtonProps) {
  return ;
}

5) Минимальные тесты (unit)

Примеры тестов с Jest + React Testing Library:

  • Проверить, что при клике на Increment счёт увеличивается.
  • Проверить, что при клике на Reset счёт возвращается в 0.

Пример (псевдо-код):

render();
fireEvent.click(screen.getByText('Increment'));
expect(screen.getByText(/Count:/)).toHaveTextContent('Count: 1');

Частые ошибки и как их исправлять

  • Неправильный импорт компонента: import Button from “./components/Botton” — исправьте на Button и переименуйте файл, если нужно.
  • Некорректное использование состояния в асинхронном окружении — используйте функциональную форму setState.
  • Отсутствие aria-live на динамическом тексте — ухудшает доступность.
  • Модификация состояния напрямую (например, count++ без setCount) — нарушает принцип иммутабельности.

Дополнения по UX и структуре

  • Покажите отключённую кнопку «Decrease» при значении 0, если не хотите отрицательных значений.
  • Добавьте анимацию или визуальный отклик для лучшего UX.
  • Держите компоненты маленькими: Button — отдельный компонент, Header или Display — отдельный.

Критерии приёмки

  1. Приложение запускается командой npm start и открывается на localhost:3000.
  2. При клике Increment значение увеличивается на 1.
  3. При клике Decrement значение уменьшается на 1 (или кнопка отключена при 0, если реализовано).
  4. При клике Reset значение становится 0.
  5. Компонент Button переиспользуется три раза и импортируется корректно.
  6. Есть базовый тест, покрывающий хотя бы один сценарий (например, Increment).

Чек-листы (роль‑ориентированные)

Разработчик:

  • Создал проект create-react-app
  • Имплементировал App и Button
  • Использовал setState корректно
  • Добавил aria-live для счётчика
  • Написал один unit-тест

Кодревьювер:

  • Проверил корректность импортов и имён файлов
  • Проверил отсутствие мутаций состояния
  • Проверил доступность (a11y)
  • Проверил простоту и читаемость кода

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

  • Разделяй и властвуй: выделяйте UI в мелкие компоненты.
  • Single source of truth: храните состояние там, где оно логически принадлежит.
  • Предсказуемость изменений: отдавайте предпочтение чистым функциям и reducer-трансформациям.

Малый playbook для расширения

  1. Если потребуется сохранять значение между перезагрузками — используйте localStorage и синхронизацию через useEffect.
  2. Если количество действий вырастает — переходите на useReducer.
  3. Для глобального состояния (несколько страниц) — рассмотрите Context или менеджеры состояний.

Примеры расширений и когда они нужны

  • Когда счётчик должен синхронизироваться между вкладками — нужен shared worker или синхронизация через backend.
  • Когда состояние становится сложным (несколько полей) — useReducer предпочтительнее.

Быстрое сравнение: useState vs useReducer

  • useState: простые, локальные состояния (числа, строки, небольшие объекты).
  • useReducer: сложная логика обновления, много типов действий, лучше тестируется.

Небольшие советы по производительности

  • Для очень частых обновлений обёрните обработчики в useCallback, если передаёте их глубоко вниз.
  • Не оптимизируйте преждевременно; сначала измерьте.

Пример принятого кода с улучшениями (итоговый компактный вариант)

import React, { useState } from "react";
import Button from "./components/Button";

function App() {
  const [count, setCount] = useState(0);

  const increment = () => setCount(prev => prev + 1);
  const decrement = () => setCount(prev => Math.max(0, prev - 1));
  const reset = () => setCount(0);

  return (
    

Count: {count}

); } export default App;

Короткий список тестов и критерии приёмки (технические)

  • Тест 1: Increment увеличивает значение на 1.
  • Тест 2: Decrement уменьшает значение на 1, но не уходит ниже 0 (если такое поведение ожидается).
  • Тест 3: Reset устанавливает 0.

Заключение

Создание простого счётчика на React — отличное упражнение для освоения компонентов, props, состояния и хуков. После реализации базового варианта стоит улучшить доступность, добавить тесты и рассмотреть useReducer/TypeScript, если требования к коду усложняются. Начиная с небольшой структуры и применяя описанные практики, вы получите читаемое, тестируемое и расширяемое приложение.

Важные заметки:

  • Исправляйте опечатки в путях/имёнах файлов при ошибках импорта.
  • Используйте функциональную форму setState, когда обновление зависит от предыдущего состояния.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство