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

Как решишь задачу получения данных с Backend в два этапа?

2.0 Middle🔥 171 комментариев
#Soft Skills и рабочие процессы

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

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

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

Как решишь задачу получения данных с Backend в два этапа?

Понимание задачи

Двухэтапное получение данных — это паттерн, когда мы сначала загружаем быстро доступные данные (например, список), а потом загружаем детальную информацию. Это улучшает perceived performance и User Experience.

Подход 1: Sequential Requests (последовательные запросы)

Сначала загружаем список, потом детали:

// React Query / TanStack Query
function UserProfile({ userId }) {
  // Этап 1: получаем базовые данные
  const { data: user } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => api.getUser(userId)
  });

  // Этап 2: получаем детали (зависит от этапа 1)
  const { data: userDetails } = useQuery({
    queryKey: ['userDetails', userId],
    queryFn: () => api.getUserDetails(userId),
    enabled: !!user // запускается только когда user загружен
  });

  if (!user || !userDetails) return <Spinner />;
  
  return <div>Имя: {user.name}, Статус: {userDetails.status}</div>;
}

Подход 2: Параллельные запросы с Priority

Если этапы независимы, загружаем параллельно:

function Dashboard() {
  // Оба запроса идут параллельно
  const userQuery = useQuery({
    queryKey: ['user'],
    queryFn: api.getUser
  });

  const statsQuery = useQuery({
    queryKey: ['stats'],
    queryFn: api.getStats
  });

  // Показываем частичные данные
  return (
    <div>
      {userQuery.data && <UserCard user={userQuery.data} />}
      {statsQuery.data && <Stats data={statsQuery.data} />}
    </div>
  );
}

Подход 3: Стратегия "быстрые данные" + "полные данные"

Грузим сначала лайтовую версию, потом тяжелую:

function ProductList() {
  // Этап 1: получаем список (быстро, 100KB)
  const { data: products } = useQuery({
    queryKey: ['products-lite'],
    queryFn: () => api.getProducts({ fields: ['id', 'name', 'price'] })
  });

  // Этап 2: загружаем full data в фоне (медленнее, 1MB)
  useQuery({
    queryKey: ['products-full'],
    queryFn: () => api.getProducts({ fields: ['*'] }),
    staleTime: 5 * 60 * 1000
  });

  return (
    <ul>
      {products?.map(p => (
        <li key={p.id}>{p.name} - {p.price}RUB</li>
      ))}
    </ul>
  );
}

Подход 4: Effector (State Management)

const $users = createStore([]);
const $userDetails = createStore({});

const getUsers = createEffect(async () => {
  return await api.getUsers();
});

const getUserDetails = createEffect(async (userId) => {
  return await api.getUserDetails(userId);
});

// Этап 1: загружаем пользователей
sample({
  clock: getUsers.doneData,
  target: $users
});

// Этап 2: когда пользователи загружены, загружаем первого
sample({
  clock: $users,
  filter: (users) => users.length > 0,
  fn: (users) => users[0].id,
  target: getUserDetails
});

sample({
  clock: getUserDetails.doneData,
  target: $userDetails
});

Подход 5: GraphQL (один запрос, структурированные данные)

const GET_USER_WITH_DETAILS = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      # Lazy load детали
      details {
        bio
        avatar
        createdAt
      }
    }
  }
`;

function UserProfile({ userId }) {
  const { data, loading } = useQuery(GET_USER_WITH_DETAILS, {
    variables: { id: userId }
  });

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

Обработка ошибок и состояний

function SafeDataFetch({ userId }) {
  const userQuery = useQuery({
    queryKey: ['user', userId],
    queryFn: api.getUser,
    retry: 3 // повтори 3 раза
  });

  const detailsQuery = useQuery({
    queryKey: ['details', userId],
    queryFn: api.getDetails,
    enabled: !!userQuery.data
  });

  if (userQuery.isLoading) return <Spinner />;
  if (userQuery.error) return <Error msg={userQuery.error.message} />;
  if (detailsQuery.error) return <ErrorBoundary />;

  return <div>{userQuery.data.name}</div>;
}

Best Practices

  1. Показывай прогресс — не оставляй пользователя без обратной связи
  2. Кешируй данные — не переделывай запросы ненужно
  3. Обработай ошибки на каждом этапе — не падай с одной ошибки
  4. Используй skeleton loaders — показывай плейсхолдеры вместо спиннеров
  5. Отмени запросы при unmount — не утекай память

Заключение

Выбор подхода зависит от архитектуры:

  • Sequential — когда второе зависит от первого
  • Parallel — когда независимы
  • Staggered — когда нужна быстрая feedback
  • GraphQL — когда хочешь один запрос

Главное — всегда думать о UX: показывать прогресс, кешировать данные и правильно обрабатывать ошибки.