NextJs 13 Experimental App Dir Hash в маршрутах, не направляющих на хэш-идентификатор
Я использую Nextjs 13 с экспериментальным App Dir, но не уверен, что эта проблема, с которой я сталкиваюсь, имеет какое-либо отношение к проблеме, с которой я сталкиваюсь. У меня есть идентификатор на моей домашней странице «Часто задаваемые вопросы», и когда я нажимаю на ссылку, я вижу, что она успешно переходит по этой ссылке, но ничего не делает в браузере. Если я нахожусь на другой странице, я нажимаю на ссылку, и она перенаправляет меня на домашнюю страницу с правильным URL-адресом, но все еще остается в верхней части страницы и не прокручивается до указанного идентификатора. я реализовалscroll={false}
как предлагается в документации, но это не имеет значения.
Вот фрагмент соответствующих частей кода:
"use client"
import React, { useState } from "react"
import { useRouter } from "next/navigation"
import Link from "next/link"
const Navigation = () => {
const router = useRouter()
...
В ответ:
<Link scroll={false} href="/#faqs">FAQS</Link>
Я даже пробовал:
<button type="button" onClick={() => router.push("/#faqs")}>FAQS</button>
В React хэш работает довольно хорошо, но в next js даже только при клиентском рендеринге он кажется запутанным. Если кто-нибудь знает, что я делаю неправильно или есть жизнеспособная работа, я был бы очень признателен. Заранее спасибо. Если я что-то упустил, пожалуйста, дайте мне знать.
3 ответа
Я часто использую хэштеги и планирую начать использовать каталог приложений в будущих проектах, поэтому я покопался в этом, и это некрасиво. Судя по всему, NextJS использует другой пакет для компонентов каталога приложений на стороне клиента, который называется «next/navigation». Это сильно отличается от «следующий/маршрутизатор». Кроме того, при использовании элементов «next/link» NextJS не запускаетonRouteChangeComplete
событие, когдаlocation.hash
меняется, ноlocation.pathname
не.
Итак, чтобы обнаружить изменение хеша и прокрутить до связанного элемента, мне, наконец, пришлось реализовать этот хак:
"use client"
import { Inter } from '@next/font/google'
import paragraph from './paragraph'
import Link from 'next/link'
import { useEffect, useState } from 'react'
const inter = Inter({ subsets: ['latin'] })
export default function Home() {
const [navClick, setNavClick] = useState(false);
useEffect(() => {
setTimeout(() => {
const hash = window.location.hash;
if (hash) document.querySelector(hash).scrollIntoView();
}, 0);
}, [navClick])
const toggleNavClick = () => setNavClick((oldVal) => !oldVal);
return (
<main>
<nav>
<ul>
<li>
<Link href="/#one" onClick={toggleNavClick}>Section One</Link>
</li>
<li>
<Link href="/#two" onClick={toggleNavClick}>Section Two</Link>
</li>
<li>
<Link href="/#three" onClick={toggleNavClick}>Section Three</Link>
</li>
</ul>
</nav>
<div className="container">
<section id="one">
<h1>Section One</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="two">
<h1>Section Two</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
<section id="three">
<h1>Section Three</h1>
<div dangerouslySetInnerHTML={{ __html: paragraph }} />
</section>
</div>
</main>
)
}
Поскольку изменение хэша не может быть обнаружено, поскольку событие не запускается, я в основном создал событие, переключивnavClick
при каждом переходе по ссылке. Логика навигации заключена вsetTimeout()
функция, потому что она срабатывает послеwindow.location
обновляется.
Репозиторий: https://github.com/designly1/next-hash-test Демо: https://next-hash-test.vercel.app/
Мой обходной путь — не использовать компонент Link:
<a href="#faqs">FAQS</a>
Мое решение для прокрутки к конкретному заголовку таково.
const scrollToHeader = (anchorId: string) => {
const headerElement = document.getElementById(anchorId);
if (headerElement) {
const offset = 100;
const scrollPosition = headerElement.getBoundingClientRect().top + window.scrollY - offset;
window.scrollTo({
top: scrollPosition,
behavior: "smooth",
});
}
};
<Link
key={headerId}
className="text-sm font-semibold text-primary hover:underline"
href={`#${headerId}`}
scroll={false}
onClick={(e) => {
scrollToHeader(headerId);
onLinkClick?.();
}}>
{parser(String(formattedHeaderText))}
</Link>