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

Какие аргументы принимает useEffect?

1.3 Junior🔥 101 комментариев
#Другое#Фреймворки и библиотеки

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

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

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

useEffect: Аргументы и правила использования

Это React хук для работы с побочными эффектами. Как backend разработчик, я часто взаимодействую с frontend через API и понимаю, как useEffect влияет на запросы к серверу.

Синтаксис

useEffect(() => {
  // Побочный эффект
  return () => {
    // Очистка (cleanup функция)
  };
}, [dependencies]);

Аргументы

1. Функция эффекта (обязателен)

useEffect(() => {
  console.log('Компонент отрендерился или зависимости изменились');
  
  // Здесь выполняются побочные эффекты:
  // - API запросы
  // - Подписка на события
  // - Изменение DOM
  // - Таймеры
});

2. Массив зависимостей (опционально)

Контролирует когда выполняется эффект:

// Без массива — выполняется после каждого рендера
useEffect(() => {
  console.log('Выполнится после КАЖДОГО рендера');
});

// Пустой массив — выполняется один раз при монтировании
useEffect(() => {
  console.log('Выполнится один раз при монтировании');
  // Идеально для инициализации, загрузки начальных данных
}, []);

// С зависимостями — выполняется если они изменились
useEffect(() => {
  fetch(`/api/users/${userId}`);
  console.log('Выполнится если userId изменится');
}, [userId]);

Функция очистки (Cleanup)

Вторая функция внутри эффекта выполняется перед удалением компонента или перед следующим эффектом:

useEffect(() => {
  // Подписка
  const subscription = eventEmitter.subscribe('user-update', handleUpdate);
  
  // Таймер
  const timer = setTimeout(() => {
    console.log('Отложенное действие');
  }, 1000);
  
  // Cleanup функция
  return () => {
    console.log('Очистка перед удалением или перед следующим эффектом');
    subscription.unsubscribe();
    clearTimeout(timer);
  };
}, []);

Примеры использования

Загрузка данных с API

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let isMounted = true; // Защита от memory leak
    
    const loadUser = async () => {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();
        
        if (isMounted) {
          setUser(data);
          setError(null);
        }
      } catch (err) {
        if (isMounted) {
          setError(err.message);
        }
      } finally {
        if (isMounted) setLoading(false);
      }
    };

    loadUser();

    // Cleanup
    return () => {
      isMounted = false; // Предотвращение вызова setState после unmount
    };
  }, [userId]); // Перезагружаем если userId изменится

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  return <div>User: {user.name}</div>;
}

WebSocket подписка

useEffect(() => {
  const ws = new WebSocket('ws://localhost:3000/notifications');
  
  ws.onmessage = (event) => {
    const notification = JSON.parse(event.data);
    console.log('Новое уведомление:', notification);
  };
  
  ws.onerror = (error) => {
    console.error('WebSocket ошибка:', error);
  };
  
  // Cleanup
  return () => {
    ws.close();
  };
}, []);

Частые ошибки

  1. Бесконечный цикл — забыли массив зависимостей

    // ❌ Плохо: выполняется при каждом рендере
    useEffect(() => {
      setData(newData); // Вызовет перерендер → новый эффект
    });
    
    // ✅ Хорошо
    useEffect(() => {
      setData(newData);
    }, []);
    
  2. Memory leak — забыли очистку при unmount

    // ❌ Плохо: setTimeout продолжит работать после удаления
    useEffect(() => {
      setTimeout(() => setData(newData), 1000);
    }, []);
    
    // ✅ Хорошо: очищаем таймер
    useEffect(() => {
      const timer = setTimeout(() => setData(newData), 1000);
      return () => clearTimeout(timer);
    }, []);
    
  3. Race conditions при асинхронных запросах

    // ❌ Плохо: если userId меняется быстро, старый запрос перезапишет новые данные
    useEffect(() => {
      fetch(`/api/users/${userId}`).then(setUser);
    }, [userId]);
    
    // ✅ Хорошо: отменяем старые запросы
    useEffect(() => {
      let isMounted = true;
      fetch(`/api/users/${userId}`)
        .then(res => res.json())
        .then(data => isMounted && setUser(data));
      return () => { isMounted = false; };
    }, [userId]);
    

Best Practices

  • Используй AbortController для отмены запросов
  • Группируй связанные эффекты в отдельные useEffect
  • Не забывай про cleanup функции для подписок
  • Перепроверяй массив зависимостей (используй плагин eslint-plugin-react-hooks)
  • Для сложной логики — рассмотри useReducer

Понимание useEffect критично, так как он напрямую влияет на количество запросов к моему API и обработку асинхронных операций.

Какие аргументы принимает useEffect? | PrepBro