EvolCode
треки/Веб от нуля до сайта·02 / 11
7 мин чтения

useState — компонент с памятью

Компонент с props — статичен: что передали, то показал. useState — это «память» компонента. Счётчик, текст в форме, открыто ли меню — всё это state. Когда state меняется, React перерисовывает компонент.

Базовый useState

$ const [count, setCount] = useState(0)

это деструктуризация массива из двух элементов: текущее значение и функция-сеттер. 0 — начальное значение. При вызове setCount(5) — React: 1) обновляет state на 5, 2) ПЕРЕЗАПУСКАЕТ функцию компонента, 3) перерисовывает DOM с новым значением. Никогда не меняй state напрямую count = 5 — ничего не произойдёт, нужно ИМЕННО setCount.

Несколько useState

В одном компоненте можно много useState — для каждого «куска памяти» свой. Например в форме регистрации:

$ const [name, setName] = useState(""); const [email, setEmail] = useState("");

Каждый изолирован. Если данные связаны (объект user с многими полями) — лучше один useState с объектом, и обновлять через spread:

$ setUser({...user, name: "новое"})

Реактивность — главное правило

НИКОГДА не мутируй state. arr.push(x) — не сработает (тот же массив, React не понимает). Правильно: setArr([...arr, x]). С объектом: obj.name = "x" плохо, setObj({...obj, name: "x"}) хорошо. Это потому что React сравнивает СТАРОЕ и НОВОЕ значение по ссылке. Та же ссылка — нет изменений → нет перерисовки.

События — onClick, onChange

Связь UI с state — через события.

$ <button onClick={() => setCount(count + 1)}>+1</button>

Для полей ввода:

$ <input value={text} onChange={(e) => setText(e.target.value)} />

Это называется «controlled input» — value привязано к state, onChange его обновляет. Без этого React теряет контроль над содержимым поля.

Функция в setter — для безопасности

Когда новое значение зависит от старого — используй колбэк-форму: setCount(c => c + 1) вместо setCount(count + 1). Это ВСЕГДА получит АКТУАЛЬНОЕ значение, даже если несколько setCount подряд. Если просто count + 1 — может получиться race condition в редких случаях.

примерtsx
import { useState } from 'react';

// === Простой счётчик ===
function Counter() {
  const [count, setCount] = useState(0);

  return (
    <>
      <p>Текущее значение: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
      <button onClick={() => setCount(c => c - 1)}>-1</button>
      <button onClick={() => setCount(0)}>Сброс</button>
    </>
  );
}

// === Контролируемый input ===
function NameForm() {
  const [name, setName] = useState('');

  return (
    <form>
      <input
        value={name}
        onChange={(e) => setName(e.target.value)}
        placeholder="Ваше имя"
      />
      <p>Привет, {name || 'Гость'}!</p>
    </form>
  );
}

// === State объектом ===
function ProfileForm() {
  const [user, setUser] = useState({ name: '', email: '', age: 0 });

  function update(field, value) {
    setUser({ ...user, [field]: value });   // СПРЕД + замена одного поля
  }

  return (
    <form>
      <input
        placeholder="Имя"
        value={user.name}
        onChange={(e) => update('name', e.target.value)}
      />
      <input
        placeholder="Email"
        type="email"
        value={user.email}
        onChange={(e) => update('email', e.target.value)}
      />
      <input
        placeholder="Возраст"
        type="number"
        value={user.age}
        onChange={(e) => update('age', +e.target.value)}
      />
      <pre>{JSON.stringify(user, null, 2)}</pre>
    </form>
  );
}

// === Список с добавлением и удалением ===
function TodoList() {
  const [items, setItems] = useState<string[]>([]);
  const [text, setText] = useState('');

  function add() {
    if (!text.trim()) return;
    setItems([...items, text]);     // НОВЫЙ массив, не push
    setText('');
  }

  function remove(idx) {
    setItems(items.filter((_, i) => i !== idx));   // НОВЫЙ массив
  }

  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <button onClick={add}>Добавить</button>
      <ul>
        {items.map((item, i) => (
          <li key={i}>
            {item} <button onClick={() => remove(i)}>×</button>
          </li>
        ))}
      </ul>
    </div>
  );
}


// === Тогглы и булевы ===
function Toggle() {
  const [on, setOn] = useState(false);
  return (
    <button onClick={() => setOn(!on)}>
      {on ? '🌞 День' : '🌙 Ночь'}
    </button>
  );
}

useState на разных задачах

главное
  • 01useState возвращает [значение, setter] — деструктурируешь массив
  • 02setter вызывает перерисовку компонента, прямое присваивание — нет
  • 03Объекты и массивы в state ВСЕГДА обновляй через spread/filter/map — никогда мутацией
  • 04<input value={state} onChange={fn} /> — controlled input, обязателен в формах
  • 05Колбэк-форма setX(c => c+1) когда новое зависит от старого — безопаснее
  • 06Несколько useState — нормально; связанные данные лучше в одном объекте
  • 07Прямое присваивание count = 5 НЕ сработает — нужно ИМЕННО setCount
проверь себя
01У вас в state массив. Хотите добавить элемент. Что НЕ сработает?
02Зачем нужна форма setCount(c => c + 1) если можно setCount(count + 1)?
03<input> без value и onChange. В чём проблема?
следующий урок: Формы — поля ввода и валидация