React Route v6. Как предотвратить переход пользователя на другую страницу

Вопрос или проблема

Я делаю проект на React и TypeScript. Я не могу найти, как остановить пользователя от перехода на другую страницу, если есть несохранённые изменения. Чтобы прояснить, я понимаю, как сделать остальное, мне просто нужно что-то, что остановит процесс, пока не получит подтверждение от пользователя. Буду признателен за ссылки на решения для V6 React-Router. Также я готов рассмотреть любые другие варианты вместо React-Router для решения этой проблемы. Я использовал BeforeUnloadEvent, но он работает только если пользователь перезагружает страницу или переходит на другой сайт. Если пользователь меняет страницы внутри приложения, это не работает.

Я использую:

"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.26.2",
"react-scripts": "^5.0.1",
"typescript": "^5.6.2"
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';

Я пробовал разные useEffects, что-то вроде:

useEffect(() => {
  if (isDirty) {          
    setIsOpen(true);
    setNextLocation(location.pathname);
  }
}, [location.pathname]);

Но я не понимаю, как добавить туда “остановить или приостановить навигацию”. Поскольку это v6, многие решения, которые работали в интернете, не работают для него, такие как Prompt, usePrompt, .listen, .block, BeforeUnload. Также я пробовал

useBeforeUnload(
  useCallback(e => {
    if (isDirty) {
      e.preventDefault();
      return '';
    }
  }, [isDirty])
); 

но это тоже не остановит навигацию (это делает то же, что и BeforeUnloadEvent).

может попробовать реализовать пользовательский хук, который использует хук useEffect вместе с подходом usePrompt??

Ответ или решение

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

Шаг 1: Создание пользовательского хука для управления навигацией

Создайте файл usePrompt.ts, который будет содержать логику для блокировки навигации:

import { useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';

const usePrompt = (message: string, when: boolean) => {
  const navigate = useNavigate();
  const location = useLocation();
  const handleBlockedNavigation = (e: BeforeUnloadEvent | any) => {
    if (when) {
      e.preventDefault();
      return (e.returnValue = message);
    }
  };

  useEffect(() => {
    window.addEventListener('beforeunload', handleBlockedNavigation);

    return () => {
      window.removeEventListener('beforeunload', handleBlockedNavigation);
    };
  }, [when]);

  useEffect(() => {
    const unblock = navigate(location.pathname, { replace: true }) // изменения необходимы для создания хуков
    return () => {
      unblock(); // освобождаем ресурс блокировки
    }
  }, [location]);

  useEffect(() => {
    if (when) {
      const confirmTransition = (e: PopStateEvent) => {
        if (when && !window.confirm(message)) {
          e.preventDefault();
          navigate(location.pathname); // возвращаем пользователя на текущую страницу
        }
      };

      window.onpopstate = confirmTransition;
      return () => {
        window.onpopstate = null;
      };
    }
  }, [when, message, navigate, location]);
};

export default usePrompt;

Шаг 2: Использование хука в компоненте

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

import React, { useState } from 'react';
import usePrompt from './usePrompt';

const MyComponent: React.FC = () => {
  const [isDirty, setIsDirty] = useState(false);

  // Задавайте нужное сообщение
  usePrompt('У вас есть несохраненные изменения. Вы действительно хотите покинуть страницу?', isDirty);

  const handleSave = () => {
    // Логика сохранения изменений
    setIsDirty(false);
  };

  // Логика изменения состояния, когда пользователь вводит данные
  const handleInputChange = () => {
    setIsDirty(true);
  };

  return (
    <div>
      <input type="text" onChange={handleInputChange} />
      <button onClick={handleSave}>Сохранить</button>
    </div>
  );
};

export default MyComponent;

Объяснение работы кода

  1. Создание пользовательского хука (usePrompt): Этот хук инициирует слушатель для события beforeunload, оно срабатывает при попытке закрыть или перезагрузить страницу. Также добавляется обработчик событий для обработки переходов внутри приложения.

  2. Обработка навигации: Когда пользователь пытается уйти со страницы, если isDirty (состояние, указывающее на наличие несохраненных изменений) истинно, будет показано сообщение с подтверждением.

  3. Использование в компоненте: В вашем компоненте нужно отслеживать состояние очищаемых данных и вызывать usePrompt, передавая текст сообщения и состояние.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...