Повторные запросы и обновление данных

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

Загрузка данных через loaders

Каждый маршрут может определять loader — функцию, возвращающую данные, необходимые компоненту для рендеринга. Loader вызывается при первом переходе на маршрут и при необходимости обновления данных. Пример определения loader:

import { createRouter, createRoute } from '@tanstack/router'

const userRoute = createRoute({
  path: '/user/:id',
  loader: async ({ params }) => {
    const response = await fetch(`/api/users/${params.id}`)
    if (!response.ok) throw new Error('Ошибка загрузки пользователя')
    return response.json()
  }
})

Важная особенность: loader вызывается автоматически при изменении параметров маршрута (params) или при вызове методов повторного запроса.

Повторные запросы с useLoader

Для получения данных внутри компонента используется хук useLoader:

import { useLoader } from '@tanstack/router'

function UserPage() {
  const { data, reload, isLoading, error } = useLoader(userRoute.loader)

  if (isLoading) return 
Загрузка...
if (error) return
Ошибка: {error.message}
return (

{data.name}

) }

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

  • reload() инициирует повторный вызов loader, позволяя обновить данные без изменения маршрута.
  • isLoading указывает на состояние ожидания ответа.
  • error содержит объект ошибки при неудачном запросе.

Автоматическое обновление данных

TanStack Router поддерживает стратегию автоматического обновления данных при изменении контекста маршрута:

  • При смене параметров маршрута: если URL изменяется, loader вызывается повторно для загрузки новых данных.
  • При возврате к маршруту: TanStack Router может использовать кэшированные данные, избегая лишних сетевых запросов, если кэш не истек.

Пример использования кэширования с автообновлением:

const userRoute = createRoute({
  path: '/user/:id',
  loader: async ({ params }) => {
    const response = await fetch(`/api/users/${params.id}`)
    return response.json()
  },
  // Тайм-аут кэша: 30 секунд
  cacheTime: 30000
})

Обновление данных на основе подписок

Для более динамичных приложений можно использовать подписки на изменения состояния или внешние события. TanStack Router позволяет интегрировать подписки с loader через invalidate:

import { router } from './router'

function onUserUpdated(userId) {
  router.invalidate(userRoute.path, { params: { id: userId } })
}

Метод invalidate инициирует повторный вызов loader и обновление данных на всех компонентах, использующих этот маршрут. Это особенно полезно для реактивного обновления UI после изменения данных на сервере.

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

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

  • Принудительное обновление (reload) — вызов loader независимо от кэша.
  • Инвалидирование кэша (invalidate) — обновление данных с проверкой, какие маршруты зависят от устаревших данных.
  • Автоматическое обновление при переходе (refetchOnMount) — опция, позволяющая loader автоматически обновлять данные при монтировании компонента.

Пример настройки автоматического обновления:

const postsRoute = createRoute({
  path: '/posts',
  loader: fetchPosts,
  options: {
    refetchOnMount: true
  }
})

Обработка ошибок и повторные попытки

Loader может быть обернут в логику повторных попыток при временных сбоях сервера:

async function fetchWithRetry(url, retries = 3) {
  for (let attempt = 0; attempt < retries; attempt++) {
    try {
      const response = await fetch(url)
      if (!response.ok) throw new Error('Ошибка сети')
      return await response.json()
    } catch (error) {
      if (attempt === retries - 1) throw error
    }
  }
}

Такой подход совместим с loader TanStack Router, обеспечивая надёжное получение данных даже при нестабильном соединении.

Комбинирование нескольких источников данных

При сложных маршрутах, требующих несколько асинхронных запросов, рекомендуется использовать Promise.all внутри loader:

const dashboardRoute = createRoute({
  path: '/dashboard',
  loader: async () => {
    const [stats, notifications] = await Promise.all([
      fetch('/api/stats').then(res => res.json()),
      fetch('/api/notifications').then(res => res.json())
    ])
    return { stats, notifications }
  }
})

Это позволяет минимизировать время ожидания и оптимизировать загрузку компонентов с множеством зависимостей данных.