Хук useState для функциональных компонентов

useState — это хук, который позволяет функциональным компонентам хранить и изменять внутреннее состояние. В отличие от классовых компонентов, где состояние определяется через this.state и изменяется с помощью this.setState, функциональные компоненты с useState становятся полноценными носителями состояния без необходимости создания классов.

import { h } from 'preact';
import { useState } from 'preact/hooks';

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

  return (
    <div>
      <p>Счетчик: {count}</p>
      <button onCl ick={() => setCount(count + 1)}>Увеличить</button>
    </div>
  );
}

В этом примере:

  • count — текущее значение состояния.
  • setCount — функция для его изменения.
  • useState(0) — инициализация состояния значением 0.

Синтаксис и принцип работы

useState всегда возвращает массив из двух элементов:

  1. Текущее состояние — значение, доступное для рендера.
  2. Функция обновления состояния — функция, которая принимает новое значение или функцию для вычисления нового значения на основе текущего.
const [state, setState] = useState(initialValue);

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

setCount(prevCount => prevCount + 1);

Это важно для правильного обновления состояния при множественных вызовах в одном рендере.

Инициализация состояния

useState позволяет задавать начальное значение различными способами:

  1. Прямое значение:
const [text, setText] = useState('Привет');
  1. Ленивое вычисление с функцией:
const [value, setValue] = useState(() => expensiveComputation());

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

Обновление состояния

Состояние в Preact обновляется асинхронно и вызывает повторный рендер компонента. Прямое присвоение переменной не влияет на рендер:

let count = 0;
count += 1; // рендер не обновится

Только вызов setCount уведомляет Preact о необходимости обновить компонент:

setCount(count + 1);

Можно передавать функцию для вычисления нового значения на основе предыдущего:

setCount(prev => prev + 1);

Это гарантирует корректное обновление при нескольких последовательных вызовах.

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

Часто состояние представляет собой сложные структуры: массивы, объекты. При этом необходимо создавать новые объекты, а не изменять существующие:

const [items, setItems] = useState([]);

function addItem(item) {
  setItems(prevItems => [...prevItems, item]);
}

Для объектов:

const [user, setUser] = useState({ name: 'Иван', age: 30 });

function updateAge(newAge) {
  setUser(prevUser => ({ ...prevUser, age: newAge }));
}

Прямое изменение объекта, например user.age = 31, не вызовет рендер компонента.

Несколько состояний

В Preact можно использовать несколько независимых состояний в одном компоненте:

const [count, setCount] = useState(0);
const [text, setText] = useState('');

Каждое состояние управляется отдельно и может обновляться независимо, что упрощает логику компонентов.

Особенности поведения

  • Состояние сохраняется между рендерами. Каждый вызов useState привязан к конкретному порядку вызовов хуков.
  • Порядок вызовов важен. Изменение порядка вызовов useState внутри компонента приведет к некорректному поведению.
  • Функции обновления состояния стабильны. Они не меняются между рендерами, что позволяет использовать их в useEffect и колбэках без дополнительных зависимостей.

Практические примеры

Простой таймер

import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prev => prev + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  return <div>Прошло секунд: {seconds}</div>;
}

Форма с управляемыми полями

function Form() {
  const [formData, setFormData] = useState({ name: '', email: '' });

  const handleChange = e => {
    const { name, value } = e.target;
    setFormData(prev => ({ ...prev, [name]: value }));
  };

  return (
    <form>
      <input name="name" value={formData.name} onIn put={handleChange} />
      <input name="email" value={formData.email} onIn put={handleChange} />
    </form>
  );
}

Выводы о useState

  • Позволяет функциональным компонентам хранить состояние.
  • Обновления через setState вызывают повторный рендер.
  • Поддерживает любые типы данных: примитивы, объекты, массивы.
  • Использование функций для обновления состояния обеспечивает корректность при множественных вызовах.
  • Несколько состояний могут сосуществовать независимо в одном компоненте, что повышает гибкость архитектуры.

useState является фундаментальным инструментом для создания интерактивных компонентов в Preact, позволяя функциональным компонентам вести себя так же динамично, как классовые с this.state.