← Назад к вопросам

Что такое useEffect в React и как правильно его использовать?

1.0 Junior🔥 271 комментариев
#React

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое useEffect в React и как правильно его использовать?

useEffect — это React Hook, который позволяет выполнять побочные эффекты (side effects) в функциональных компонентах. Побочный эффект — это любая операция, которая влияет на мир вне компонента: запросы к API, подписки, изменение DOM и т.д.

Синтаксис

useEffect(() => {
  // Код побочного эффекта выполняется после каждого рендера
  console.log('Компонент отрисован');
}, [dependencies]);

useEffect принимает два аргумента:

  1. Функция эффекта — код, который нужно выполнить
  2. Массив зависимостей — опционально, контролирует когда запускается эффект

Три варианта использования

1. Без массива зависимостей

Эффект запускается после каждого рендера:

function Example() {
  useEffect(() => {
    console.log('Рендер произошёл');
  });

  return <h1>Привет</h1>;
}

// Выполнится после каждого рендера

Это опасно и обычно вызывает бесконечные циклы:

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    setCount(count + 1); // Бесконечный цикл!
  }); // Нет зависимостей

  return <div>{count}</div>;
}

2. С пустым массивом зависимостей

Эффект запускается только один раз при монтировании компонента:

function FetchUser() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch('/api/user')
      .then(res => res.json())
      .then(data => setUser(data));
  }, []); // Пустой массив — только при монтировании

  return <div>{user?.name}</div>;
}

3. С массивом зависимостей

Эффект запускается только когда меняются зависимости:

function SearchUsers({ query }) {
  const [results, setResults] = useState([]);

  useEffect(() => {
    if (query.length === 0) return;

    fetch(`/api/search?q=${query}`)
      .then(res => res.json())
      .then(data => setResults(data));
  }, [query]); // Запускается при изменении query

  return <ul>{results.map(r => <li>{r.name}</li>)}</ul>;
}

Очистка (Cleanup)

Если эффект создаёт ресурсы (подписки, таймеры), нужна функция очистки:

function ChatWindow({ userId }) {
  useEffect(() => {
    // Подписываемся на сообщения
    const unsubscribe = subscribeToMessages(userId, (msg) => {
      console.log(msg);
    });

    // Возвращаем функцию очистки
    return () => {
      unsubscribe(); // Отписываемся при размонтировании или перед новым эффектом
    };
  }, [userId]);

  return <div>Chat</div>;
}

Очистка выполняется:

  • Перед новым выполнением эффекта (если зависимости изменились)
  • Перед размонтированием компонента

Проблемы и как их избежать

1. Бесконечные циклы

// ПЛОХО: Эффект создаёт новый объект, что вызывает новый эффект
useEffect(() => {
  setData({ items: [] });
}, [data]); // data меняется в эффекте!

// ХОРОШО: Пустой массив — эффект только один раз
useEffect(() => {
  setData({ items: [] });
}, []);

2. Закрытые переменные (stale closures)

// ПЛОХО: count всегда 0 в таймере
function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => {
      console.log(count); // Всегда 0
    }, 1000);
    return () => clearInterval(timer);
  }, []); // count не в зависимостях

  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

// ХОРОШО: count в зависимостях
useEffect(() => {
  const timer = setInterval(() => {
    console.log(count); // Правильное значение
  }, 1000);
  return () => clearInterval(timer);
}, [count]); // count добавлен в зависимости

3. Утечки памяти

// ПЛОХО: Таймер не очищается
useEffect(() => {
  setInterval(() => {
    console.log('tick');
  }, 1000);
}, []);

// ХОРОШО: Таймер очищается
useEffect(() => {
  const id = setInterval(() => {
    console.log('tick');
  }, 1000);
  return () => clearInterval(id);
}, []);

Правила использования useEffect

  1. Всегда включай все зависимости в массив (используй eslint-plugin-react-hooks)
  2. Разделяй эффекты — каждый эффект должен отвечать за одно
  3. Используй useCallback или useMemo если эффект зависит от функций/объектов:
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  // ПЛОХО: fetchUser пересоздаётся каждый рендер
  useEffect(() => {
    const fetchUser = async () => {
      const res = await fetch(`/api/users/${userId}`);
      setUser(await res.json());
    };
    fetchUser();
  }, [userId]); // Но функция не в зависимостях!

  // ХОРОШО: Используем useCallback
  const fetchUser = useCallback(async () => {
    const res = await fetch(`/api/users/${userId}`);
    setUser(await res.json());
  }, [userId]);

  useEffect(() => {
    fetchUser();
  }, [fetchUser]);
}

Практические примеры

Запрос данных:

function BlogPost({ postId }) {
  const [post, setPost] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    setLoading(true);
    fetch(`/api/posts/${postId}`)
      .then(res => {
        if (!res.ok) throw new Error('Failed');
        return res.json();
      })
      .then(data => setPost(data))
      .catch(err => setError(err.message))
      .finally(() => setLoading(false));
  }, [postId]);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  return <article>{post?.content}</article>;
}

Синхронизация с внешним хранилищем:

function ThemeProvider() {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    // Сохраняем в localStorage при изменении
    localStorage.setItem('theme', theme);
    document.documentElement.className = theme;
  }, [theme]);

  // Загружаем из localStorage при монтировании
  useEffect(() => {
    const saved = localStorage.getItem('theme');
    if (saved) setTheme(saved);
  }, []);

  return <button onClick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>Toggle</button>;
}

Вывод

useEffect — это мощный инструмент для управления побочными эффектами в React. Главное правило: всегда указывай правильный массив зависимостей, правильно очищай ресурсы и разделяй эффекты по логике. Правильное использование useEffect — это основа построения надёжных React приложений.

Что такое useEffect в React и как правильно его использовать? | PrepBro