useState — это хук в React, который добавляет локальное состояние в функциональные компоненты. Он возвращает текущее значение состояния и функцию для его обновления.
Синтаксис: const [state, setState] = useState(initialValue);
Ключевые особенности:
Пример использования:
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Вы кликнули {count} раз</p>
<button onClick={() => setCount(count + 1)}>
Увеличить счетчик
</button>
</div>
);
}Хук useState — один из основных инструментов React, введенных в версии 16.8. Он позволяет добавлять и управлять состоянием в функциональных компонентах, что раньше было возможно только в классовых компонентах с использованием this.state. 🚀
useState возвращает массив из двух элементов:
import React, { useState } from 'react';
function Example() {
// Деструктуризация массива, возвращаемого useState
const [count, setCount] = useState(0);
console.log('Текущее состояние:', count);
return (
<div>
<p>Значение: {count}</p>
<button onClick={() => setCount(count + 1)}>
Увеличить
</button>
</div>
);
}Когда вызывается функция обновления состояния (setCount в примере выше), React:
Если новое состояние зависит от предыдущего, лучше использовать функциональную форму:
function Counter() {
const [count, setCount] = useState(0);
const handleIncrement = () => {
// ❌ Может быть проблемой при быстрых кликах
// setCount(count + 1);
// ✅ Гарантирует использование актуального состояния
setCount(prevCount => prevCount + 1);
};
return (
<button onClick={handleIncrement}>
Увеличить: {count}
</button>
);
}Если начальное состояние требует вычислений, можно передать функцию:
function ExpensiveInitialState() {
// Функция вызывается только при первом рендере
const [state, setState] = useState(() => {
console.log('Вычисление начального состояния...');
return calculateExpensiveValue();
});
return (
<div>
<p>Значение: {state}</p>
<button onClick={() => setState(state + 1)}>
Обновить
</button>
</div>
);
}React использует Object.is для сравнения значений:
function ObjectStateExample() {
const [person, setPerson] = useState({ name: 'Иван', age: 30 });
const updateAge = () => {
// ❌ Не вызовет ререндер, если возраст тот же
// person.age = 31;
// setPerson(person);
// ✅ Создает новый объект, вызывает ререндер
setPerson({ ...person, age: 31 });
};
return (
<div>
<p>Имя: {person.name}, Возраст: {person.age}</p>
<button onClick={updateAge}>
Обновить возраст
</button>
</div>
);
}function FormExample() {
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const [isActive, setIsActive] = useState(false);
return (
<form>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Введите имя"
/>
<input
type="number"
value={age}
onChange={e => setAge(Number(e.target.value))}
placeholder="Введите возраст"
/>
<label>
<input
type="checkbox"
checked={isActive}
onChange={e => setIsActive(e.target.checked)}
/>
Активен
</label>
</form>
);
}function TodoList() {
const [todos, setTodos] = useState([]);
const [input, setInput] = useState('');
const addTodo = () => {
if (input.trim()) {
// Создаем новый массив с новым элементом
setTodos([...todos, input]);
setInput('');
}
};
const removeTodo = (index) => {
// Фильтруем массив, исключая элемент по индексу
setTodos(todos.filter((_, i) => i !== index));
};
return (
<div>
<input
value={input}
onChange={e => setInput(e.target.value)}
/>
<button onClick={addTodo}>Добавить</button>
<ul>
{todos.map((todo, index) => (
<li key={index}>
{todo}
<button onClick={() => removeTodo(index)}>Удалить</button>
</li>
))}
</ul>
</div>
);
}function UserProfile() {
const [profile, setProfile] = useState({
firstName: '',
lastName: '',
email: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
// Обновляем только изменившееся поле, сохраняя остальные
setProfile(prevProfile => ({
...prevProfile,
[name]: value
}));
};
return (
<form>
<input
name="firstName"
value={profile.firstName}
onChange={handleChange}
placeholder="Имя"
/>
<input
name="lastName"
value={profile.lastName}
onChange={handleChange}
placeholder="Фамилия"
/>
<input
name="email"
value={profile.email}
onChange={handleChange}
placeholder="Email"
/>
<div>
<strong>Профиль:</strong>
{JSON.stringify(profile, null, 2)}
</div>
</form>
);
}// Классовый компонент
class ClassCounter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
render() {
return (
<div>
<p>Счетчик: {this.state.count}</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Увеличить
</button>
</div>
);
}
}
// Функциональный компонент с useState
function HookCounter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Счетчик: {count}</p>
<button onClick={() => setCount(count + 1)}>
Увеличить
</button>
</div>
);
}// ❌ Много отдельных состояний
function UserFormBad() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
// Много обработчиков
}
// ✅ Объединение связанных состояний
function UserFormGood() {
const [user, setUser] = useState({
firstName: '',
lastName: '',
email: '',
phone: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setUser(prevUser => ({
...prevUser,
[name]: value
}));
};
// Один обработчик для всех полей
}function ExpensiveCalculation({ data }) {
const [count, setCount] = useState(0);
// Здесь тяжелое вычисление вызывается при каждом рендере
const expensiveResult = calculateExpensive(data);
return (
<div>
<p>Результат: {expensiveResult}</p>
<button onClick={() => setCount(count + 1)}>
Счетчик: {count}
</button>
</div>
);
}
// Решение: использовать useMemo вместе с useState
function OptimizedCalculation({ data }) {
const [count, setCount] = useState(0);
// Вычисление выполняется только при изменении data
const expensiveResult = useMemo(() => {
return calculateExpensive(data);
}, [data]);
return (
<div>
<p>Результат: {expensiveResult}</p>
<button onClick={() => setCount(count + 1)}>
Счетчик: {count}
</button>
</div>
);
}// ❌ Неправильно: прямое мутирование
function WrongMutation() {
const [user, setUser] = useState({ name: 'Иван', age: 30 });
const handleClick = () => {
// Это НЕ вызовет ререндер!
user.age = 31;
setUser(user);
};
return (
<div>
<p>Имя: {user.name}, Возраст: {user.age}</p>
<button onClick={handleClick}>Обновить возраст</button>
</div>
);
}
// ✅ Правильно: создание нового объекта
function CorrectUpdate() {
const [user, setUser] = useState({ name: 'Иван', age: 30 });
const handleClick = () => {
// Создаем новый объект
setUser({ ...user, age: 31 });
};
return (
<div>
<p>Имя: {user.name}, Возраст: {user.age}</p>
<button onClick={handleClick}>Обновить возраст</button>
</div>
);
}// ❌ Неправильно: полагаться на немедленное обновление
function AsyncProblem() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
console.log(count); // Здесь всё еще старое значение!
};
return (
<button onClick={handleClick}>
Увеличить: {count}
</button>
);
}
// ✅ Правильно: использовать useEffect для реакции на изменения
function CorrectAsync() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Счетчик обновлен:", count);
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>
Увеличить: {count}
</button>
);
}// ❌ Неправильно: может привести к пропуску обновлений
function MultipleUpdatesWrong() {
const [count, setCount] = useState(0);
const handleMultipleClicks = () => {
// Все эти вызовы используют одно и то же значение count
setCount(count + 1); // count = 0 -> 1
setCount(count + 1); // count = 0 -> 1
setCount(count + 1); // count = 0 -> 1
// Результат: count = 1, а не 3!
};
return (
<button onClick={handleMultipleClicks}>
Несколько обновлений: {count}
</button>
);
}
// ✅ Правильно: использовать функциональную форму
function MultipleUpdatesCorrect() {
const [count, setCount] = useState(0);
const handleMultipleClicks = () => {
// Каждый вызов получает актуальное предыдущее значение
setCount(prev => prev + 1); // 0 -> 1
setCount(prev => prev + 1); // 1 -> 2
setCount(prev => prev + 1); // 2 -> 3
// Результат: count = 3
};
return (
<button onClick={handleMultipleClicks}>
Несколько обновлений: {count}
</button>
);
}✅ useState — основной хук React для:
this.state и this.setState из классовых компонентов✅ Ключевые особенности:
const [state, setState] = useState(initialValue)setState(prev => newValue)useState(() => computeValue())✅ Лучшие практики:
useState — фундаментальный хук React, который делает функциональные компоненты более мощными и гибкими, позволяя им управлять внутренним состоянием так же эффективно, как и классовые компоненты. 🚀
Хотите больше статей для подготовки к собеседованиям? Подписывайтесь на EasyAdvice, добавляйте сайт в закладки и совершенствуйтесь каждый день 💪