useBlocker для предотвращения перехода

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


Основы работы

В React Router маршруты и навигация управляются объектом истории. Когда происходит переход (например, вызов navigate("/home") или клик по <Link>), React Router инициирует процесс смены маршрута. useBlocker позволяет вмешаться в этот процесс, предоставляя контроль над навигацией до того, как она завершится.

Синтаксис базового использования:

import { useBlocker } from "react-router-dom";

function MyComponent() {
  const blocker = useBlocker((tx) => {
    if (!window.confirm("Вы действительно хотите покинуть страницу?")) {
      tx.retry(); // Продолжение навигации после подтверждения
    }
  });

  return null;
}
  • tx — объект транзакции навигации, предоставляемый React Router, содержащий методы retry() и cancel().
  • retry() — возобновляет заблокированную навигацию.
  • cancel() — отменяет переход.

Хук вызывается при каждой попытке изменения маршрута, независимо от того, инициирована ли она программно через navigate или через пользовательское взаимодействие с <Link>.


Применение в формах и предупреждениях

Наиболее распространённый сценарий использования useBlocker — предотвращение ухода со страницы с несохранёнными данными. Пример интеграции с формой:

import { useBlocker } from "react-router-dom";
import { useState, useEffect } from "react";

function FormPage() {
  const [isDirty, setIsDirty] = useState(false);

  const blocker = useBlocker((tx) => {
    if (isDirty) {
      const confirmLeave = window.confirm(
        "У вас есть несохранённые изменения. Вы уверены, что хотите покинуть страницу?"
      );
      if (confirmLeave) tx.retry();
    } else {
      tx.retry();
    }
  });

  return (
    <form>
      <input
        type="text"
        onCha nge={() => setIsDirty(true)}
        placeholder="Введите данные"
      />
    </form>
  );
}

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

  • Проверка состояния формы (isDirty) перед блокировкой навигации.
  • Вызов tx.retry() после подтверждения пользователя для продолжения перехода.
  • Возможность полностью отменить переход без дальнейших действий.

Блокировка программной навигации

useBlocker блокирует не только пользовательские клики по ссылкам, но и программные вызовы navigate(). Это позволяет централизованно управлять всеми сценариями навигации:

import { useNavigate, useBlocker } from "react-router-dom";

function Dashboard() {
  const navigate = useNavigate();

  useBlocker((tx) => {
    if (!window.confirm("Покинуть страницу?")) {
      // Отмена перехода
    } else {
      tx.retry();
    }
  });

  return <button onCl ick={() => navigate("/settings")}>Перейти в настройки</button>;
}

Блокировка действует независимо от того, инициирована ли навигация через <Link> или через кнопку с navigate.


Особенности и подводные камни

  1. Синхронность и асинхронность useBlocker работает синхронно для вызова подтверждений (например, window.confirm). Для асинхронных проверок нужно аккуратно управлять состоянием и запускать навигацию через tx.retry() после завершения проверки.

  2. Множественные блокировки Если на странице подключено несколько блокеров, React Router выполняет их по порядку. Отмена хотя бы одного блокера предотвращает навигацию полностью.

  3. Не блокирует перезагрузку страницы или закрытие вкладки Для обработки этих сценариев используется нативное событие beforeunload в сочетании с useEffect.

  4. Совместимость с React Router v6+ Хук useBlocker доступен в новых версиях React Router, заменяя устаревшие подходы с Prompt.


Рекомендации по использованию

  • Использовать useBlocker только на страницах, где есть риск потери данных или нежелательных переходов.
  • Сохранять логику проверки в отдельные функции для читаемости и повторного использования.
  • Всегда предоставлять пользователю возможность отмены или подтверждения перехода, чтобы не нарушать UX.

Пример структурирования:

function confirmNavigation(isDirty, tx) {
  if (isDirty) {
    const confirmLeave = window.confirm(
      "У вас есть несохранённые изменения. Вы уверены, что хотите покинуть страницу?"
    );
    if (confirmLeave) tx.retry();
  } else {
    tx.retry();
  }
}

function FormPage() {
  const [isDirty, setIsDirty] = useState(false);
  useBlocker((tx) => confirmNavigation(isDirty, tx));
}

Такой подход упрощает тестирование и поддержку кода при большом количестве форм или страниц с блокировками.


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