Пошук уроків, статей та іншого контенту
Дізнаємося, як правильно оновлювати State, передавати нове значення та працювати з попереднім станом.
У React стан зберігає дані, від яких залежить вигляд компонента. Коли стан змінюється, React повторно відображає компонент із новими значеннями.
Для оновлення стану, створеного за допомогою useState, потрібно використовувати функцію-оновлювач, яку повертає цей хук:
const [state, setState] = useState(initialValue);state — поточне значення стану;
setState — функція для оновлення стану;
initialValue — початкове значення.
Не слід змінювати змінну стану напряму. Замість цього потрібно викликати setState.
Якщо нове значення не залежить від попереднього, його можна передати безпосередньо у функцію-оновлювач.
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
function resetCount() {
setCount(0);
}
return (
<div>
<p>Лічильник: {count}</p>
<button onClick={() => setCount(10)}>
Встановити 10
</button>
<button onClick={resetCount}>
Скинути
</button>
</div>
);
}
export default Counter;Після виклику setCount(10) значення count стане 10, а компонент буде повторно відображено.
Виклик функції-оновлювача не змінює змінну в поточному виконанні функції компонента:
function Example() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(1);
console.log(count); // 0
}
return <button onClick={handleClick}>Оновити</button>;
}У цьому прикладі виведеться 0, тому що поточний запуск компонента ще працює зі старим значенням. Нове значення буде доступне під час наступного відображення компонента.
Часто нове значення залежить від попереднього. Наприклад, щоб збільшити лічильник на одиницю, потрібно взяти його поточне значення.
Для цього передайте у функцію-оновлювач функцію:
setCount((previousCount) => previousCount + 1);previousCount — це гарантовано актуальне попереднє значення стану.
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
function increase() {
setCount((previousCount) => previousCount + 1);
}
function decrease() {
setCount((previousCount) => previousCount - 1);
}
return (
<div>
<p>Лічильник: {count}</p>
<button onClick={decrease}>−</button>
<button onClick={increase}>+</button>
</div>
);
}
export default Counter;Якщо потрібно виконати кілька оновлень, що залежать одне від одного, використовуйте функціональну форму оновлення:
function increaseThreeTimes() {
setCount((previousCount) => previousCount + 1);
setCount((previousCount) => previousCount + 1);
setCount((previousCount) => previousCount + 1);
}Кожна функція отримує результат попереднього оновлення. Тому значення збільшиться на 3.
Такий запис може бути помилковим:
function increaseThreeTimes() {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
}Усі три виклики використовують одне й те саме значення count із поточного відображення компонента. У результаті стан може збільшитися лише на 1.
Стан може містити об'єкт:
const [user, setUser] = useState({
name: "Олена",
age: 25,
});Щоб змінити одну властивість, потрібно створити новий об'єкт і скопіювати в нього старі властивості за допомогою оператора розпилення ...:
setUser((previousUser) => ({
...previousUser,
age: previousUser.age + 1,
}));Повний приклад:
import { useState } from "react";
function UserProfile() {
const [user, setUser] = useState({
name: "Олена",
age: 25,
});
function increaseAge() {
setUser((previousUser) => ({
...previousUser,
age: previousUser.age + 1,
}));
}
return (
<div>
<p>Ім'я: {user.name}</p>
<p>Вік: {user.age}</p>
<button onClick={increaseAge}>
Збільшити вік
</button>
</div>
);
}
export default UserProfile;Якщо не скопіювати старі властивості, вони зникнуть:
setUser({
age: 26,
});Після такого оновлення об'єкт матиме лише властивість age. Властивість name буде втрачена.
Для масивів також потрібно створювати новий масив, а не змінювати існуючий.
setTasks((previousTasks) => [
...previousTasks,
newTask,
]);Для видалення можна використати filter:
setTasks((previousTasks) =>
previousTasks.filter((task) => task.id !== taskId)
);Для зміни одного елемента зручно використати map:
setTasks((previousTasks) =>
previousTasks.map((task) =>
task.id === taskId
? { ...task, completed: true }
: task
)
);Приклад компонента зі списком завдань:
import { useState } from "react";
function TaskList() {
const [tasks, setTasks] = useState([
{ id: 1, title: "Вивчити state", completed: false },
{ id: 2, title: "Написати компонент", completed: false },
]);
function toggleTask(taskId) {
setTasks((previousTasks) =>
previousTasks.map((task) =>
task.id === taskId
? { ...task, completed: !task.completed }
: task
)
);
}
function addTask() {
const newTask = {
id: Date.now(),
title: "Повторити матеріал",
completed: false,
};
setTasks((previousTasks) => [
...previousTasks,
newTask,
]);
}
return (
<div>
<button onClick={addTask}>Додати завдання</button>
<ul>
{tasks.map((task) => (
<li key={task.id}>
<span>
{task.title} — {task.completed ? "виконано" : "не виконано"}
</span>
<button onClick={() => toggleTask(task.id)}>
Змінити стан
</button>
</li>
))}
</ul>
</div>
);
}
export default TaskList;Використовуйте готове значення, коли воно не залежить від попереднього стану:
setIsVisible(true);
setName("Марко");
setPage(1);Використовуйте функціональну форму, коли нове значення залежить від попереднього:
setCount((previousCount) => previousCount + 1);
setIsVisible((previousValue) => !previousValue);
setItems((previousItems) => [...previousItems, newItem]);Функціональна форма є особливо важливою для лічильників, перемикачів, списків та інших значень, які змінюються на основі попереднього стану.
Неправильно:
count = count + 1;Неправильно також змінювати об'єкт або масив напряму:
user.age = user.age + 1;
tasks.push(newTask);Правильно:
setCount((previousCount) => previousCount + 1);
setUser((previousUser) => ({
...previousUser,
age: previousUser.age + 1,
}));
setTasks((previousTasks) => [
...previousTasks,
newTask,
]);Неправильно:
setUser({ age: 26 });Правильно:
setUser((previousUser) => ({
...previousUser,
age: 26,
}));Неправильно:
setCount(count + 1);
setCount(count + 1);Правильно:
setCount((previousCount) => previousCount + 1);
setCount((previousCount) => previousCount + 1);Неправильно очікувати, що змінна одразу міститиме нове значення:
setCount(10);
console.log(count);Поточна змінна count залишиться старою до наступного відображення компонента.
Для оновлення стану використовуйте функцію-оновлювач, яку повертає useState.
Нове незалежне значення можна передати напряму:
setValue(newValue);Якщо нове значення залежить від попереднього, використовуйте функцію:
setValue((previousValue) => newValue);Не змінюйте стан напряму.
Під час оновлення об'єктів копіюйте попередні властивості через ....
Під час оновлення масивів створюйте новий масив за допомогою map, filter або оператора розпилення.
Виклик оновлювача запланує повторне відображення компонента, але не змінить значення в поточному виконанні функції.