Подтверждение ухода со страницы

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

Использование exitGuard для контроля ухода

Основным инструментом для реализации подтверждения ухода со страницы является функция exitGuard. Она позволяет задать проверку перед сменой маршрута. Если проверка возвращает false или промис, который отклоняется, переход будет отменён.

Пример базовой конфигурации:

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

const router = createRouter({
  routeTree: rootRoute,
});

const formRoute = new Route({
  path: '/form',
  component: FormComponent,
  exitGuard: (route, next) => {
    if (route.meta.isFormDirty) {
      return window.confirm('У вас есть несохранённые изменения. Вы уверены, что хотите уйти?');
    }
    return true;
  },
});

В этом примере:

  • exitGuard вызывается при попытке уйти с текущего маршрута.
  • Проверяется состояние route.meta.isFormDirty.
  • Если форма изменена, вызывается стандартное окно подтверждения window.confirm.
  • Возврат true разрешает переход, false его блокирует.

Асинхронная проверка перед уходом

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

exitGuard: async (route, next) => {
  if (route.meta.isFormDirty) {
    const shouldLeave = await fetch('/api/check-unsaved', {
      method: 'POST',
      body: JSON.stringify({ formId: route.meta.formId }),
    }).then(res => res.json());
    return shouldLeave.confirmed;
  }
  return true;
}

Особенности:

  • exitGuard может возвращать промис.
  • Переход откладывается до завершения промиса.
  • Это позволяет интегрировать логику проверки с серверной частью.

Глобальные перехватчики переходов

Помимо локальных exitGuard на уровне маршрута, можно использовать глобальные перехватчики на уровне роутера. Это полезно для реализации единой логики защиты всех маршрутов, содержащих формы или важные данные.

router.addExitGuard(async (route, next) => {
  if (route.meta.isFormDirty) {
    return window.confirm('Есть несохранённые изменения. Продолжить?');
  }
  return true;
});

Особенности глобальных перехватчиков:

  • Применяются ко всем маршрутам.
  • Могут сочетаться с локальными exitGuard.
  • Упрощают поддержку единого поведения при уходе с различных страниц приложения.

Связывание состояния с маршрутом

Для корректной работы подтверждений ухода важно хранить флаг “грязной формы” в состоянии маршрута. TanStack Router позволяет использовать объект meta маршрута для этих целей:

const formRoute = new Route({
  path: '/form',
  component: FormComponent,
  meta: {
    isFormDirty: false,
  },
});

При изменении формы:

function handleInputChange() {
  formRoute.meta.isFormDirty = true;
}

Преимущества такого подхода:

  • Состояние “грязной формы” привязано к маршруту.
  • Переходы учитывают текущее состояние данных.
  • Исключается необходимость глобальных переменных для отслеживания изменений.

Особенности UX и ограничения

  • window.confirm блокирует выполнение до пользовательского решения, поэтому рекомендуется использовать кастомные модальные окна для улучшения пользовательского опыта.
  • Асинхронные exitGuard могут замедлять переходы, если длительное взаимодействие с сервером.
  • Для SPA важно учитывать сохранение состояния маршрутов, чтобы meta корректно обновлялось при возврате на предыдущие страницы.

Практическая интеграция с React

В React компонентах можно использовать useEffect для обновления состояния маршрута при изменении формы:

import { useEffect } from 'react';
import { useRoute } from '@tanstack/router';

function FormComponent() {
  const route = useRoute();

  useEffect(() => {
    const handleChange = () => {
      route.meta.isFormDirty = true;
    };
    document.querySelector('form').addEventListener('input', handleChange);
    return () => {
      document.querySelector('form').removeEventListener('input', handleChange);
    };
  }, [route]);
  
  return 
{/* поля формы */}
; }

Преимущества:

  • Обновление состояния маршрута синхронизировано с действиями пользователя.
  • Гарантируется, что при попытке ухода состояние будет проверено через exitGuard.

Комбинирование с другими хуками роутера

TanStack Router позволяет комбинировать exitGuard с хуками beforeLoad и afterLoad, что открывает возможности для более сложной логики, например, сохранения данных на сервере при уходе:

beforeLoad: async (route) => {
  if (route.meta.isFormDirty) {
    await saveFormData(route.meta.formData);
  }
}

Эта комбинация делает роутинг не просто навигацией, а полноценным инструментом управления состоянием приложения.