Fallback-компоненты

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


Основы использования

В TanStack Router каждая маршрутизируемая точка может содержать loader или component, возвращающий промис. Если промис не завершён, рендеринг основного компонента блокируется. Для решения этой проблемы используется свойство fallback, куда передаётся компонент ожидания.

Пример базового использования:

import { Router, Route } from '@tanstack/router';

const Loading = () => <div>Загрузка...</div>;

const router = new Router({
  routeTree: new Route({
    path: '/',
    component: HomePage,
    loader: async () => {
      const data = await fetchData();
      return data;
    },
    fallback: <Loading />
  })
});

Здесь Loading будет отображаться до завершения loader.


Асинхронные компоненты

TanStack Router поддерживает асинхронную загрузку компонентов. Для этого используется динамический импорт с React.lazy и Suspense. В сочетании с fallback это обеспечивает плавный пользовательский опыт без “мёртвого экрана”.

Пример:

import React, { Suspense } from 'react';
import { Router, Route } from '@tanstack/router';

const LazyComponent = React.lazy(() => import('./LazyComponent'));
const Loading = () => <div>Загрузка...</div>;

const router = new Router({
  routeTree: new Route({
    path: '/lazy',
    component: () => (
      <Suspense fallback={<Loading />}>
        <LazyComponent />
      </Suspense>
    )
  })
});

Использование Suspense позволяет централизовать управление fallback-компонентами при динамических загрузках, что снижает дублирование кода.


Fallback для вложенных маршрутов

TanStack Router позволяет определять fallback не только для отдельных маршрутов, но и для целых веток маршрутов. Если у родительского маршрута задан fallback, он автоматически применяется ко всем дочерним маршрутам, которые ещё не загрузились.

const Parent = new Route({
  path: '/parent',
  component: ParentComponent,
  fallback: <ParentLoading />,
  children: [
    new Route({ path: 'child', component: ChildComponent }),
    new Route({ path: 'child2', component: Child2Component }),
  ]
});

Здесь при переходе на /parent/child или /parent/child2 будет отображён ParentLoading до полной готовности дочерних компонентов.


Динамическая подстановка fallback

Fallback-компоненты могут быть динамическими, с логикой изменения в зависимости от состояния загрузки данных. Например, можно показывать разные индикаторы в зависимости от типа запроса:

const DataFallback = ({ resourceType }) => {
  if (resourceType === 'user') return <div>Загрузка данных пользователя...</div>;
  if (resourceType === 'posts') return <div>Загрузка постов...</div>;
  return <div>Загрузка...</div>;
};

const router = new Router({
  routeTree: new Route({
    path: '/dashboard',
    component: Dashboard,
    loader: async ({ context }) => await fetchDashboardData(context),
    fallback: <DataFallback resourceType="dashboard" />
  })
});

Особенности взаимодействия с loader

loader в TanStack Router возвращает данные, которые автоматически передаются компоненту через пропсы. Если loader ещё не завершился, отображается fallback. После завершения промиса происходит автоматический рендер основного компонента с результатами loader.

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

  • Fallback не блокирует загрузку данных, он просто визуально информирует о процессе.
  • Можно комбинировать несколько уровней fallback, если есть вложенные маршруты с асинхронной загрузкой.
  • Fallback может быть любым React-компонентом: простой текст, спиннер, анимация или сложный компонент с логикой.

Управление fallback глобально

Для приложений с большим количеством маршрутов имеет смысл определить единый глобальный fallback через контекст или HOC. Например, создаётся AppFallback и оборачивается в корневой маршрутизатор:

const AppFallback = () => <div>Приложение загружается...</div>;

const router = new Router({
  routeTree: rootRoute,
  defaultFallback: <AppFallback />
});

Все маршруты без собственного fallback будут использовать AppFallback.


Лучшая практика

  1. Использовать компактные и информативные fallback-компоненты, чтобы минимизировать раздражение пользователей.
  2. Для вложенных маршрутов назначать fallback на уровне родителя, если дочерние маршруты имеют асинхронные загрузки.
  3. При динамическом импорте компонентов всегда комбинировать React.lazy и Suspense.
  4. Если данные критичны для UI, использовать fallback с минимальным скелетоном вместо пустого экрана.

Fallback-компоненты в TanStack Router обеспечивают гибкое и удобное управление состояниями загрузки, делают приложение отзывчивым и позволяют создать плавный пользовательский опыт даже при масштабных асинхронных операциях.