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

Как управлять асинхронными запросами в React?

1.7 Middle🔥 221 комментариев
#React#Архитектура и паттерны

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

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

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

Управление асинхронными запросами в React

В современном React управление асинхронными запросами — это критически важный навык, так как большинство приложений взаимодействуют с API. Я применяю многоуровневый подход в зависимости от сложности приложения, предпочитая комбинацию встроенных возможностей React и специализированных библиотек.

Основные подходы и инструменты

1. Использование встроенных возможностей React

useEffect + useState — классическая пара для простых случаев:

import { useState, useEffect } from 'react';

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

  useEffect(() => {
    const fetchUser = async () => {
      setLoading(true);
      setError(null);
      
      try {
        const response = await fetch(`/api/users/${userId}`);
        if (!response.ok) throw new Error('Network response was not ok');
        const data = await response.json();
        setUser(data);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchUser();
  }, [userId]); // Зависимость — перезапрос при изменении userId

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

Ключевые моменты:

  • useEffect запускает запрос при монтировании и изменении зависимостей
  • Состояние загрузки и ошибок — обязательная практика
  • Очистка (cleanup) важна для предотвращения утечек памяти

2. Специализированные хуки и библиотеки

Для продвинутых сценариев я использую:

  • React Query (TanStack Query) — мой основной выбор для сложных приложений
  • SWR — отличная альтернатива для более простых случаев
  • RTK Query — если проект уже использует Redux Toolkit

Пример с React Query:

import { useQuery } from '@tanstack/react-query';

function UserProfile({ userId }) {
  const { data: user, isLoading, error } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetch(`/api/users/${userId}`).then(res => res.json()),
    staleTime: 5 * 60 * 1000, // 5 минут
    retry: 3,
  });

  // React Query автоматически управляет кешированием, повторными запросами
  // и обновлениями в фоновом режиме
}

Паттерны и лучшие практики

Разделение ответственности

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

// api/users.js
export const userAPI = {
  fetchUser: async (userId) => {
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
  },
  
  updateUser: async (userId, data) => {
    const response = await fetch(`/api/users/${userId}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    });
    return response.json();
  },
};

// hooks/useUser.js
export const useUser = (userId) => {
  return useQuery({
    queryKey: ['user', userId],
    queryFn: () => userAPI.fetchUser(userId),
  });
};

Управление состоянием асинхронных операций

Для CRUD-операций я следую этим принципам:

  • Оптимистичные обновления — мгновенное отражение изменений в UI
  • Откат при ошибке — возврат к предыдущему состоянию
  • Индикаторы состояния — визуальная обратная связь для пользователя
const { mutate, isLoading } = useMutation({
  mutationFn: (newData) => userAPI.updateUser(userId, newData),
  onMutate: async (newData) => {
    // Отменяем исходящие запросы
    await queryClient.cancelQueries(['user', userId]);
    
    // Сохраняем предыдущее значение для отката
    const previousUser = queryClient.getQueryData(['user', userId]);
    
    // Оптимистичное обновление
    queryClient.setQueryData(['user', userId], newData);
    
    return { previousUser };
  },
  onError: (err, newData, context) => {
    // Откат при ошибке
    queryClient.setQueryData(['user', userId], context.previousUser);
  },
  onSettled: () => {
    // Инвалидация и перезапрос для синхронизации
    queryClient.invalidateQueries(['user', userId]);
  },
});

Архитектурные соображения

Обработка ошибок глобально

Я создаю перехватчики ошибок на уровне приложения:

// Оборачивание приложения в Error Boundary
class ErrorBoundary extends React.Component {
  state = { hasError: false, error: null };
  
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
  
  render() {
    if (this.state.hasError) {
      return <ErrorFallback error={this.state.error} />;
    }
    return this.props.children;
  }
}

Производительность и оптимизация

  • Кеширование запросов — избегание повторных запросов одинаковых данных
  • Пагинация и бесконечная прокрутка для больших наборов данных
  • Предзагрузка данных при наведении или предвидении действий пользователя

Выбор подхода

Мой выбор зависит от масштаба приложения:

  • Небольшие приложенияuseEffect + кастомные хуки
  • Средние проектыSWR для простоты и эффективности
  • Крупные enterprise-приложенияReact Query с полным набором функций
  • Проекты с ReduxRTK Query для интеграции с существующей экосистемой

Главный принцип: отделять логику данных от компонентов, обеспечивать последовательную обработку состояний (загрузка, успех, ошибка) и использовать подходящие абстракции для предотвращения boilerplate-кода. Современные библиотеки типа React Query значительно упрощают управление асинхронными операциями, беря на себя кеширование, синхронизацию, обновления и отмену запросов.

Как управлять асинхронными запросами в React? | PrepBro