useState в React — управление состоянием компонента

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

Синтаксис useState

import { useState } from 'react';

const [value, setValue] = useState(initialValue);

useState возвращает два значения: текущее состояние и функцию для его изменения. После вызова setValue React заново рендерит компонент и показывает новый интерфейс.

Основные приёмы useState

useState полезнее всего разбирать на простых интерактивных задачах: счётчик, форма, массив элементов и обновление на основе предыдущего значения. Эти приёмы потом повторяются в корзинах, фильтрах, вкладках и модальных окнах.

Не спешите переходить к Redux, пока эти базовые операции не стали привычными. Большая часть локальной интерактивности в React по-прежнему спокойно решается useState без глобального хранилища.

Пример: счётчик

import { useState } from 'react';

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

  return (
    <div>
      <p>Значение: {count}</p>
      <button onClick={() => setCount(count + 1)}>Увеличить</button>
      <button onClick={() => setCount(0)}>Сбросить</button>
    </div>
  );
}

Обновление на основе предыдущего значения

Если новое состояние зависит от старого, используйте функцию. Это особенно важно, когда несколько обновлений идут подряд.

setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);
setCount(prevCount => prevCount + 1);

Форма на useState

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  function handleSubmit(event) {
    event.preventDefault();
    console.log({ email, password });
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={email} onChange={e => setEmail(e.target.value)} />
      <input type="password" value={password} onChange={e => setPassword(e.target.value)} />
      <button>Войти</button>
    </form>
  );
}

Состояние массива

Нельзя менять массив напрямую через push или splice. Создавайте новый массив, чтобы React увидел изменение.

function TodoList() {
  const [todos, setTodos] = useState([]);

  function addTodo(text) {
    const newTodo = { id: Date.now(), text, done: false };
    setTodos(prevTodos => [...prevTodos, newTodo]);
  }

  function toggleTodo(id) {
    setTodos(prevTodos =>
      prevTodos.map(todo =>
        todo.id === id ? { ...todo, done: !todo.done } : todo
      )
    );
  }
}

Типичные ошибки

  • Менять объект или массив напрямую.
  • Ждать, что состояние изменится сразу в той же строке после setState.
  • Хранить в state данные, которые можно вычислить из других значений.
  • Создавать слишком много независимых state, когда нужен один объект формы.

Мини-проект: корзина товаров

Корзина хорошо показывает useState, потому что состояние — это массив объектов, а действия меняют количество и состав товаров.

function Cart() {
  const [items, setItems] = useState([
    { id: 1, title: 'Курс React', price: 2900, count: 1 },
  ]);

  function increase(id) {
    setItems(prev =>
      prev.map(item =>
        item.id === id ? { ...item, count: item.count + 1 } : item
      )
    );
  }

  function remove(id) {
    setItems(prev => prev.filter(item => item.id !== id));
  }

  const total = items.reduce((sum, item) => sum + item.price * item.count, 0);

  return (
    <section>
      {items.map(item => (
        <article key={item.id}>
          <b>{item.title}</b>
          <span>{item.count} шт.</span>
          <button onClick={() => increase(item.id)}>+</button>
          <button onClick={() => remove(item.id)}>Удалить</button>
        </article>
      ))}
      <p>Итого: {total} руб.</p>
    </section>
  );
}

В этом примере total не хранится в state, потому что его можно вычислить из items. Это важное правило: не дублируйте производные данные в состоянии, иначе однажды они начнут расходиться.

Как выбирать форму состояния

  • Если значения меняются независимо, можно хранить их разными useState.
  • Если значения всегда меняются вместе, удобнее объект.
  • Если есть повторяющиеся элементы, нужен массив.
  • Если обновления стали сложными, рассмотрите useReducer или Redux Toolkit.

Проверка понимания

Попробуйте добавить кнопку «Очистить корзину», ограничение количества до 10 и сообщение «Корзина пуста». Если вы можете сделать это без прямой мутации массива, базовый useState понятен.

Это упражнение хорошо тем, что сразу проверяет три вещи: обновление массива без мутации, вычисление производных значений и реакцию интерфейса на пустое состояние. Если все три пункта получаются, база useState уже крепкая.

Как понять, что состояние выбрано правильно

Главное правило useState: храните минимальный набор данных, от которого зависит интерфейс. Всё, что можно надёжно вычислить из уже имеющегося state, не нужно дублировать.

Чем меньше дублирования в state, тем меньше расхождений в интерфейсе. Например, итоговую сумму корзины лучше вычислять из массива товаров, а не хранить отдельно рядом с теми же данными.

Проверка перед сохранением в state

  • это значение меняется со временем?
  • оно влияет на то, что пользователь видит?
  • его нельзя вычислить из других значений?
  • оно принадлежит именно этому компоненту?
  • если страницу перезагрузить, его можно потерять?

Типичный лишний state

Например, total в корзине часто не нужно хранить отдельно. Его можно вычислить из items через reduce. Если хранить и items, и total, однажды они могут разъехаться.

Упражнение для закрепления

Сделайте корзину: добавить товар, увеличить количество, удалить товар, показать итоговую сумму. Если итог считается из массива, а массив не мутируется напрямую, useState используется правильно.

Оцените статью
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x