Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як правильно завантажити дані в useEffect і чому без очищення виникає гонитва?

Найпростіше завантаження в ефекті:

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

  useEffect(() => {
    let ignore = false;

    fetch(`/api/users/${userId}`)
      .then((response) => response.json())
      .then((data) => {
        if (!ignore) setUser(data);
      });

    return () => {
      ignore = true;
    };
  }, [userId]);

  if (!user) return <Spinner />;
  return <h1>{user.name}</h1>;
}

Навіщо змінна ignore. Користувач швидко перемикає профілі: userId 1, потім 2. Летять два запити. Якщо відповідь для 1 прийде пізніше за відповідь для 2, без перевірки вона перезапише стан - на екрані профіль 2-го користувача з даними 1-го. Функція очищення попереднього ефекту ставить ignore = true, і застаріла відповідь ігнорується.

Ще краще - скасувати запит:

useEffect(() => {
  const controller = new AbortController();
  fetch(`/api/users/${userId}`, { signal: controller.signal })
    .then((r) => r.json())
    .then(setUser)
    .catch((error) => {
      if (error.name !== 'AbortError') setError(error);
    });
  return () => controller.abort();
}, [userId]);

StrictMode у розробці запускає ефект двічі - з очищенням між запусками. Перший запит скасовується. Це нормально й саме перевіряє, що очищення написане.

Чого бракує цьому підходу (і чому документація React радить не писати таке вручну у великих застосунках):

  • немає кешу: повернення на сторінку - новий запит і знову спінер;
  • «водоспад» запитів: дочірні компоненти починають завантаження лише після рендеру батька;
  • стани помилки й завантаження - у кожному компоненті вручну;
  • немає попереднього завантаження і повторного запиту при поверненні на вкладку;
  • серверний рендер: ефекти на сервері не виконуються.

Альтернативи:

  • фреймворк (Next.js, React Router з завантажувачами) - дані завантажуються до рендеру маршруту;
  • TanStack Query чи SWR - кеш, повтори, скасування, синхронізація;
  • use() з Suspense і промісом зі стабільного джерела.

useEffect для даних лишається прийнятним для невеликих застосунків і простих випадків - але з очищенням.

Докладніше в документації: Синхронізація з ефектами: завантаження даних

Схожі питання