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

Есть ли в React аналоги useeffect?

2.3 Middle🔥 211 комментариев
#React

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

Есть ли в React аналоги useEffect

useEffect — это хук для управления побочными эффектами (side effects) в функциональных компонентах. В React есть несколько хуков, которые служат похожим или дополняющим целям.

useEffect и его назначение

Первый, давай разберёмся, для чего нужен useEffect:

import { useEffect, useState } from "react";

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

  // useEffect срабатывает ПОСЛЕ рендера
  useEffect(() => {
    const fetchUser = async () => {
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      setUser(data);
      setLoading(false);
    };

    fetchUser();
  }, [userId]);  // dependency array

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

useEffect используется для:

  • Загрузки данных с сервера
  • Подписки на события
  • Таймеры и интервалы
  • Работа с DOM
  • Очистка ресурсов

Аналоги и альтернативы useEffect

1. useLayoutEffect — синхронный useEffect

import { useLayoutEffect } from "react";

function MeasureComponent() {
  const [height, setHeight] = useState(0);
  const divRef = useRef(null);

  // useLayoutEffect срабатывает ПЕРЕД отрисовкой браузером
  // (после render, но ДО paint)
  useLayoutEffect(() => {
    if (divRef.current) {
      setHeight(divRef.current.offsetHeight);
    }
  }, []);

  return (
    <div>
      <div ref={divRef}>Контент</div>
      <p>Высота: {height}px</p>
    </div>
  );
}

// Сравнение:
// useEffect:       render -> paint -> effect
// useLayoutEffect: render -> effect -> paint

Когда использовать useLayoutEffect:

  • Измерение размеров DOM элементов
  • Синхронное изменение DOM (избежать мигания)
  • Фокусировка на элемент сразу же

2. useCallback — мемоизация функций

import { useCallback } from "react";

function SearchComponent() {
  const [query, setQuery] = useState("");

  // useCallback создает мемоизированную функцию
  // которая не создается заново при каждом рендере
  const handleSearch = useCallback((q) => {
    fetch(`/api/search?q=${q}`);
  }, []);  // зависимости

  return (
    <input
      onChange={(e) => handleSearch(e.target.value)}
      placeholder="Поиск..."
    />
  );
}

// Разница:
// БЕЗ useCallback:
function Component() {
  const handleClick = () => console.log("clicked");  // НОВАЯ функция каждый рендер
  return <Child onClick={handleClick} />;
}

// С useCallback:
function Component() {
  const handleClick = useCallback(
    () => console.log("clicked"),
    []
  );  // ОДНА и та же функция
  return <Child onClick={handleClick} />;
}

3. useMemo — мемоизация вычислений

import { useMemo } from "react";

function HeavyComponent({ items, filter }) {
  // useMemo кэширует результат вычисления
  // вычисление запускается только если dependencies изменились
  const filteredItems = useMemo(() => {
    console.log("Фильтрую...");
    return items.filter((item) => item.category === filter);
  }, [items, filter]);

  return (
    <ul>
      {filteredItems.map((item) => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  );
}

// Сравнение:
// БЕЗ useMemo: каждый рендер фильтрует заново (медленно)
// С useMemo: кэширует результат, вычисляет только при изменении зависимостей

4. useRef — доступ к DOM и сохранение значений

import { useRef } from "react";

function TextInput() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={focusInput}>Сфокусировать</button>
    </>
  );
}

// useRef также может сохранять значения между рендерами
function Timer() {
  const intervalRef = useRef(null);
  const [count, setCount] = useState(0);

  const startTimer = () => {
    // Сохраняем ID интервала в ref
    intervalRef.current = setInterval(() => {
      setCount((c) => c + 1);
    }, 1000);
  };

  const stopTimer = () => {
    clearInterval(intervalRef.current);
  };

  return (
    <>
      <p>{count}</p>
      <button onClick={startTimer}>Старт</button>
      <button onClick={stopTimer}>Стоп</button>
    </>
  );
}

5. useContext — доступ к контексту

import { createContext, useContext } from "react";

const ThemeContext = createContext("light");

function ThemedComponent() {
  // useContext получает значение из контекста
  const theme = useContext(ThemeContext);
  return <div className={theme}>Контент</div>;
}

// useContext заменяет useEffect для подписки на контекст
// Когда контекст изменяется, компонент ререндеривается

6. useReducer — управление сложным состоянием

import { useReducer } from "react";

const initialState = { count: 0, error: null };

function reducer(state, action) {
  switch (action.type) {
    case "INCREMENT":
      return { ...state, count: state.count + 1 };
    case "DECREMENT":
      return { ...state, count: state.count - 1 };
    case "ERROR":
      return { ...state, error: action.payload };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      <p>{state.count}</p>
      <button onClick={() => dispatch({ type: "INCREMENT" })}>
        +
      </button>
      <button onClick={() => dispatch({ type: "DECREMENT" })}>
        -
      </button>
    </>
  );
}

// useReducer + useEffect часто используются вместе
useEffect(() => {
  fetchData().catch((error) => {
    dispatch({ type: "ERROR", payload: error });
  });
}, []);

Новое в React 19: useTransition и useDeferred

import { useTransition } from "react";

function SearchResults({ query }) {
  const [isPending, startTransition] = useTransition();
  const [results, setResults] = useState([]);

  const handleSearch = (q) => {
    // startTransition говорит React: это обновление может быть slow
    startTransition(() => {
      fetchResults(q).then(setResults);
    });
  };

  return (
    <>
      {isPending && <div>Загрузка...</div>}
      <ResultList results={results} />
    </>
  );
}

// useTransition заменяет некоторые use cases useEffect
// для управления асинхронными операциями

Сравнительная таблица

ХукНазначениеКогда использовать
useEffectSide effects (загрузка данных, подписки)Асинхронные операции, таймеры, слушатели
useLayoutEffectСинхронные эффекты, измерения DOMФокусировка, измерение, синхронный DOM
useCallbackМемоизация функцийПередача коллбэков в оптимизированные компоненты
useMemoКэширование вычисленийТяжелые вычисления, фильтрация массивов
useRefДоступ к DOM, сохранение значенийПрямой доступ к элементам, таймеры
useContextДоступ к контекстуГлобальное состояние (тема, авторизация)
useReducerУправление сложным состояниемЛогика с множеством переходов состояний
useTransitionУправление приоритетом обновленийАсинхронные обновления UI

Практический пример: загрузка данных

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

  // useEffect для загрузки
  useEffect(() => {
    setLoading(true);
    fetch(`/api/users/${userId}`)
      .then((r) => r.json())
      .then((data) => setUser(data))
      .catch((err) => setError(err))
      .finally(() => setLoading(false));
  }, [userId]);

  if (loading) return <div>Загрузка...</div>;
  if (error) return <div>Ошибка: {error.message}</div>;
  return <div>{user.name}</div>;
}

Итог

useEffect — это основной хук для side effects. Но React предоставляет много других хуков для разных целей:

  • useLayoutEffect — когда нужно синхронное выполнение
  • useCallback/useMemo — для оптимизации производительности
  • useRef — для доступа к DOM
  • useContext/useReducer — для управления состоянием
  • useTransition — для приоритизации обновлений

Каждый хук имеет свою роль, и часто они используются вместе.

Есть ли в React аналоги useeffect? | PrepBro