Пошук уроків, статей та іншого контенту
Розберемо, що таке локальний стан компонента, навіщо він потрібен і чим відрізняється від звичайних змінних.
State, або стан, — це дані, які належать компоненту та можуть змінюватися під час його роботи.
Стан потрібен, коли компонент має:
запам’ятовувати дії користувача;
відображати різні дані залежно від ситуації;
оновлювати інтерфейс після зміни даних;
зберігати тимчасову інформацію, наприклад значення лічильника чи текст у полі введення.
Наприклад, лічильник має зберігати своє поточне значення. Після натискання кнопки це значення збільшується, а компонент показує оновлений результат.
У звичайному JavaScript можна створити змінну:
let count = 0;
count += 1;Значення змінної зміниться, але React не дізнається про цю зміну автоматично. Тому інтерфейс не перемалюється.
Розглянемо компонент:
function Counter() {
let count = 0;
function handleClick() {
count += 1;
console.log(count);
}
return (
<button onClick={handleClick}>
Натиснуто: {count}
</button>
);
}Після натискання count справді збільшиться в пам’яті поточного виконання функції. Але текст кнопки на екрані не зміниться.
Причини:
Зміна звичайної змінної не повідомляє React, що потрібно оновити компонент.
Під час наступного рендерингу count знову отримає значення 0.
Звичайна змінна не призначена для збереження даних між рендерами компонента.
Для таких даних використовують state.
useStateДля створення локального стану React надає хук useState.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Натиснуто: {count}
</button>
);
}
export default Counter;Розглянемо цей рядок:
const [count, setCount] = useState(0);Він містить три важливі частини:
useState(0) — створює стан із початковим значенням 0;
count — поточне значення стану;
setCount — функція для зміни стану.
Назви можна обирати інші, але зазвичай використовують таку схему:
const [значення, функціяДляЗміни] = useState(початковеЗначення);Наприклад:
const [name, setName] = useState('');
const [isOpen, setIsOpen] = useState(false);
const [items, setItems] = useState([]);Коли викликається функція зміни стану, наприклад setCount, React:
запам’ятовує нове значення;
планує повторний рендеринг компонента;
знову виконує функцію компонента;
показує в інтерфейсі актуальне значення state.
Приклад:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function increase() {
setCount(count + 1);
}
return (
<div>
<p>Поточне значення: {count}</p>
<button onClick={increase}>
Збільшити
</button>
</div>
);
}
export default Counter;Спочатку на екрані буде:
Поточне значення: 0Після натискання кнопки React викличе setCount, значення стане 1, а компонент відобразить:
Поточне значення: 1Кожне наступне натискання збільшуватиме значення.
Стан, створений усередині компонента, є локальним для цього компонента.
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
{count}
</button>
);
}
function App() {
return (
<main>
<Counter />
<Counter />
</main>
);
}
export default App;У цьому прикладі на сторінці є два компоненти Counter. Кожен має власний state:
натискання першої кнопки змінює лише її значення;
натискання другої кнопки змінює лише її значення.
Один компонент не змінює локальний state іншого компонента.
Стан не обмежується лише числами. Він може містити рядки, логічні значення, масиви або об’єкти.
import { useState } from 'react';
function Greeting() {
const [name, setName] = useState('');
return (
<div>
<input
value={name}
onChange={(event) => setName(event.target.value)}
placeholder="Введіть ім’я"
/>
<p>Привіт, {name || 'незнайомцю'}!</p>
</div>
);
}
export default Greeting;Коли користувач вводить текст, викликається setName. React оновлює state і показує нове ім’я.
import { useState } from 'react';
function Details() {
const [isVisible, setIsVisible] = useState(false);
return (
<div>
<button onClick={() => setIsVisible(!isVisible)}>
{isVisible ? 'Сховати' : 'Показати'} деталі
</button>
{isVisible && <p>Додаткова інформація доступна.</p>}
</div>
);
}
export default Details;Тут isVisible визначає, чи потрібно показувати абзац.
Не слід змінювати значення state безпосередньо:
// Неправильно
count = count + 1;Потрібно використовувати функцію, яку повертає useState:
// Правильно
setCount(count + 1);Саме виклик функції setCount повідомляє React про необхідність оновити компонент.
Якщо новий state залежить від попереднього, краще передавати у функцію оновлення callback:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
function increaseTwice() {
setCount((previousCount) => previousCount + 1);
setCount((previousCount) => previousCount + 1);
}
return (
<div>
<p>Значення: {count}</p>
<button onClick={increaseTwice}>
Збільшити двічі
</button>
</div>
);
}
export default Counter;У цьому випадку кожен виклик отримує актуальне попереднє значення. Після натискання значення збільшиться на 2.
Такий запис особливо корисний, коли:
оновлення виконуються кілька разів;
нове значення залежить від попереднього;
оновлення відбувається всередині іншої функції.
useStateДотримуйтеся таких правил:
викликайте useState лише всередині компонента або власного хука;
не викликайте useState всередині if, циклів чи вкладених функцій;
не змінюйте state напряму;
для оновлення використовуйте функцію, яку повертає useState;
зберігайте в state лише дані, які справді можуть змінюватися та впливають на інтерфейс.
Правильно:
function Example() {
const [isActive, setIsActive] = useState(false);
return (
<button onClick={() => setIsActive(true)}>
Активувати
</button>
);
}Неправильно:
function Example({ shouldUseState }) {
if (shouldUseState) {
// Неправильно: хук викликається умовно
const [value, setValue] = useState(0);
}
return <div>Приклад</div>;
}React очікує, що хуки викликатимуться в однаковому порядку під час кожного рендерингу компонента.
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
console.log(count);
}У консолі одразу після setCount може бути попереднє значення. Виклик функції оновлює state для наступного рендерингу, а не змінює змінну в поточному виконанні функції.
// Неправильно
items.push(newItem);
setItems(items);Масив був змінений безпосередньо. Краще створити новий масив:
setItems((previousItems) => [...previousItems, newItem]);Якщо значення ніколи не змінюється, його зазвичай не потрібно зберігати в state:
function Product() {
const title = 'Ноутбук';
return <h2>{title}</h2>;
}State потрібен для даних, які можуть змінитися та мають спричинити оновлення інтерфейсу.
Створимо компонент списку завдань. Він зберігатиме:
текст нового завдання;
масив доданих завдань.
import { useState } from 'react';
function TodoList() {
const [task, setTask] = useState('');
const [tasks, setTasks] = useState([]);
function handleSubmit(event) {
event.preventDefault();
const trimmedTask = task.trim();
if (!trimmedTask) {
return;
}
setTasks((previousTasks) => [
...previousTasks,
{
id: crypto.randomUUID(),
text: trimmedTask,
},
]);
setTask('');
}
return (
<section>
<h1>Список завдань</h1>
<form onSubmit={handleSubmit}>
<input
value={task}
onChange={(event) => setTask(event.target.value)}
placeholder="Нове завдання"
/>
<button type="submit">
Додати
</button>
</form>
<ul>
{tasks.map((item) => (
<li key={item.id}>{item.text}</li>
))}
</ul>
</section>
);
}
export default TodoList;У цьому прикладі:
task зберігає поточне значення поля введення;
setTask оновлює текст під час введення;
tasks зберігає всі додані завдання;
setTasks створює новий масив і додає до нього завдання;
після зміни state React повторно відображає компонент.
State — це дані компонента, які можуть змінюватися.
Для створення state використовується хук useState.
useState повертає поточне значення та функцію для його оновлення.
Виклик функції оновлення state спричиняє повторний рендеринг компонента.
Звичайна змінна не повідомляє React про зміну і не зберігає значення між рендерами.
Локальний state належить конкретному екземпляру компонента.
State не можна змінювати напряму.
Якщо нове значення залежить від попереднього, використовуйте функціональний запис оновлення.