Эффекты: useEffect
Компонент должен только вычислять разметку по пропсам и состоянию. Всё, что выходит за её пределы, — изменить заголовок вкладки, сохранить данные в localStorage, запросить сервер, запустить таймер — называют побочным эффектом. Такой код выносят в хук useEffect: React выполнит его после того, как обновит страницу.
Первый эффект
Покажем число оставшихся задач в заголовке вкладки. В src/App.jsx:
// src/App.jsx
import { useEffect, useState } from 'react';
export default function App() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Купить молоко', done: false },
{ id: 2, text: 'Позвонить Олегу', done: false },
]);
const left = todos.filter((todo) => !todo.done).length;
useEffect(() => {
document.title = `Задач: ${left}`;
});
return <p>Осталось задач: {left}</p>;
}На вкладке браузера появится «Задач: 2». Эффект без второго аргумента запускается после каждой отрисовки.
Массив зависимостей
Второй аргумент useEffect — массив зависимостей. Он говорит React, когда запускать эффект снова:
// src/App.jsx
useEffect(() => {
document.title = `Задач: ${left}`;
}, [left]);Теперь эффект сработает после первой отрисовки и затем только когда изменится left. Добавление задачи обновит заголовок, а ввод текста в поле — нет.
| Запись | Когда выполняется |
|---|---|
useEffect(fn) | После каждой отрисовки |
useEffect(fn, []) | Один раз, после первой отрисовки |
useEffect(fn, [a, b]) | После первой отрисовки и при изменении a или b |
В массив помещают все пропсы и переменные состояния, которые используются внутри эффекта. Линтер в проекте Vite подскажет, если что-то забыто.
Сохранение в localStorage
Ещё один эффект — сохранять список при каждом изменении:
// src/App.jsx
useEffect(() => {
localStorage.setItem('todos', JSON.stringify(todos));
}, [todos]);Чтобы при открытии страницы список загрузился обратно, начальное значение читают в useState через функцию — она выполнится только при первой отрисовке:
// src/App.jsx
const [todos, setTodos] = useState(() => {
const saved = localStorage.getItem('todos');
return saved ? JSON.parse(saved) : [];
});Добавьте задачу и обновите страницу — она останется в списке.
Когда эффект не нужен
Если значение можно вычислить из пропсов и состояния, эффект не нужен — вычисляйте его при отрисовке. Реакцию на клик пишите в обработчике события. Эффект нужен только для синхронизации с внешним миром: DOM вне React, хранилище, сеть, таймеры.
Частая ошибка
В режиме разработки StrictMode из src/main.jsx намеренно запускает каждый эффект дважды, чтобы проверить, умеет ли он правильно убирать за собой. Если в консоли сообщение из эффекта появилось два раза, это не ошибка — в собранном сайте эффект выполнится один раз. Не удаляйте StrictMode, чтобы от этого избавиться: лучше научить эффект очищать за собой, об этом следующий урок.