Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Эффекты: 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, чтобы от этого избавиться: лучше научить эффект очищать за собой, об этом следующий урок.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.