Определение предыдущего адреса страницы в Next.js 14 для интеллектуальной навигации

При разработке на Next.js 14 часто возникает задача определения предыдущего адреса страницы для реализации логики навигации. Например, необходимо вернуться на предыдущую страницу, если она принадлежит вашему сайту, или перенаправить на главную, если переход был осуществлен с внешнего ресурса (как в случае прямого ввода адреса www.my-site/login в браузере).

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

Рассмотрим два распространенных, но неработающих подхода, с которыми сталкиваются разработчики.

1. Неэффективность document.referrer

Использование document.referrer в Next.js оказывается бесполезным по двум причинам:

  • Свойство остается пустым при переходе с внешнего сайта на ваш
  • Оно также пустое при навигации между страницами внутри вашего приложения
  • Проверка document.referrer.includes(window.location.origin) всегда возвращает false из-за пустого значения referrer

Пример кода, демонстрирующий проблему:

"use client";
import { usePathname } from 'next/navigation';
import { useEffect} from 'react';

export default function IsExternal() {
  const pathname = usePathname();  
  useEffect(() => {   
    console.log("document.referrer", document.referrer);  // всегда пустой    
    const cameFromExternalSite = !document.referrer.includes(window.location.origin);  // всегда true 
    if(cameFromExternalSite){
      sessionStorage.setItem("prevPath", "external" );
    }    
    console.log("Внешний переход:", cameFromExternalSite);  // всегда true    
  }, [pathname]);
  return null;
}

2. Ограничения sessionStorage

Попытка использовать sessionStorage для отслеживания предыдущей страницы также сталкивается с проблемами:

  • Код записывает текущий путь вместо предыдущего
  • Невозможно корректно определить внешний переход
  • Теряется контекст навигации при обновлении страницы

Пример проблемной реализации:

"use client";
import { usePathname,  useSearchParams } from 'next/navigation';
import { useEffect, useRef } from 'react';

export default function TrackPreviousPage() {
  const pathname = usePathname();
  const prevPath = useRef(null);
  const previousPage = document.referrer;
  const currentOrigin = window.location.origin;  
  useEffect(() => {  
    if (prevPath.current) {
      sessionStorage.setItem("prevPath", prevPath.current); // записывает текущий адрес
    }
    prevPath.current = pathname;
  }, [pathname])
  return null
};

Рекомендуемое решение

Для корректного определения предыдущего адреса в Next.js 14 рекомендуется использовать комбинированный подход:

  1. Создать кастомный хук useNavigationHistory для отслеживания истории переходов
  2. Использовать состояние (state) для хранения предыдущего пути
  3. Реализовать middleware для обработки навигационных событий на серверной стороне
  4. Сохранять историю переходов в безопасном хранилище с учетом архитектуры Next.js

Ключевой момент - разделение логики для клиентских переходов (используя состояние приложения) и начальных загрузок (обрабатывая через заголовки HTTP или специальные метки).