Skip to content
Topic 3 of 5
Theory

Состояние: useState

Данные, которые меняются

Props приходят снаружи и не меняются компонентом. Но у интерфейса есть и своя память: открыто ли меню, что введено в поле, сколько раз нажали кнопку, какая вкладка выбрана. Это состояние (state). Когда состояние меняется, React сам перерисовывает компонент с новыми значениями — не нужно искать элементы и менять их текст вручную.

useState

Состояние создаётся хуком useState:

const { useState } = React;   // в проекте: import { useState } from "react";

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

  return (
    <div>
      <p>Нажато: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}
  • useState(0) — начальное значение, используется только при первом показе.
  • Возвращает пару: текущее значение count и функцию setCount, чтобы его поменять. Имена выбираешь сам, принято что-то и setЧто-то.
  • setCount(5) запоминает новое значение и просит React перерисовать компонент. При следующем вызове функции Counter useState вернёт уже 5.

У каждого экземпляра компонента своё состояние: два <Counter /> на странице считают независимо.

Почему не обычная переменная

function Broken() {
  let count = 0;                       // при каждой перерисовке снова 0
  return <button onClick={() => { count++; }}>{count}</button>;   // и React не узнает об изменении
}

Обычная переменная создаётся заново при каждом вызове функции-компонента и не говорит React, что пора перерисовать. Поэтому всё, что меняется и должно быть видно на экране, хранят в useState.

События в React

Обработчики пишутся как props в camelCase: onClick, onChange, onSubmit, onMouseEnter, onKeyDown. В них передают функцию, а не её вызов:

<button onClick={handleClick}>OK</button>             // правильно — функция
<button onClick={() => setOpen(true)}>Открыть</button>  // правильно — стрелка
<button onClick={setOpen(true)}>Открыть</button>        // ошибка: вызовется сразу при отрисовке

Обновление по предыдущему значению

setCount(count + 1) берёт count из текущей отрисовки. Если обновить несколько раз подряд, все вызовы увидят одно и то же старое значение:

setCount(count + 1);
setCount(count + 1);   // count всё ещё старый → итог +1, а не +2

Надёжный способ — передать функцию, которая получает актуальное значение:

setCount((c) => c + 1);
setCount((c) => c + 1);   // итог +2

Используй форму с функцией всегда, когда новое значение считается из старого.

Логическое состояние

Открыто / закрыто, включено / выключено — true и false:

const [dark, setDark] = useState(false);

<button onClick={() => setDark((d) => !d)}>
  {dark ? "Светлая тема" : "Тёмная тема"}
</button>

Объекты и массивы в состоянии

Состояние нельзя менять на месте (user.name = ..., items.push(...)): React сравнивает старое и новое значение, и если это тот же объект, он решит, что ничего не изменилось. Всегда создавай новый объект или массив:

const [user, setUser] = useState({ name: "Аня", age: 20 });
setUser({ ...user, age: user.age + 1 });          // копия с изменённым полем

const [items, setItems] = useState([]);
setItems([...items, newItem]);                     // добавить
setItems(items.filter((i) => i.id !== id));        // удалить
setItems(items.map((i) => (i.id === id ? { ...i, done: !i.done } : i)));   // изменить один

Это те же ..., filter и map из курса JavaScript — в React они используются постоянно.

Неактивные кнопки

Логические атрибуты передаются выражением: <button disabled={count === 0}>. Когда условие ложно, React уберёт атрибут, когда истинно — добавит.

Правила хуков

useState и другие функции, начинающиеся с use, — это хуки. Два правила:

  1. Вызывать только на верхнем уровне компонента — не внутри if, циклов и вложенных функций.
  2. Вызывать только в компонентах (или в своих хуках), не в обычных функциях.

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

Итог

  • const [value, setValue] = useState(начальное) — память компонента; setValue перерисовывает.
  • Обработчики: onClick={функция}, не вызов.
  • Новое из старого — setValue((old) => ...).
  • Объекты и массивы не меняем на месте — создаём копии через ..., map, filter.
  • Хуки — только на верхнем уровне компонента.

Счётчик

Easy+10 XP

Напиши компонент Counter: число в <span> (сначала 0) и кнопка <button> с текстом +1, которая увеличивает число.

0 [+1]  → клик →  1 [+1]  → клик →  2 [+1]

Connect the connector to solve tasks

Task code runs on your own computer — faster and more reliable than in the browser. This needs the free CodeWithMe connector app. You only connect it once.

Looking for the connector on this computer…

  1. 1
    Download the connector for your system (Windows, macOS or Linux).Download the connectorOn the download page pick the file with your system in its name, e.g. windows-amd64.
  2. 2Run it and choose a folder for your solutions. It will install Python and Node.js there by itself when needed.
  3. 3
    Enter below the 6-digit code shown in the connector window (“Code for CodeWithMe”). If the browser asks about local network access — allow it.

The connector is open source (on GitHub), works only on your computer and runs code only after you enter the pairing code.