⚓
【小ネタ】Next.jsでハッシュフラグメントを張る
anchorlink.tsx
import { FC, useEffect, useRef } from "react";
import Link from "next/link";
import { useRouter } from "next/router";
import { UrlObject } from "url";
interface Props {
to: UrlObject | string;
anchor: string;
}
const AnchorLink: FC<Props> = ({ children, to, anchor }) => {
const ref = useRef<HTMLAnchorElement>();
const router = useRouter();
useEffect(() => {
const hashChanged = (url) => {
const hash = url.split("#")[1];
if (hash !== anchor) return;
ref.current.scrollIntoView();
};
router.events.on("hashChangeStart", hashChanged);
return () => router.events.off("hashChangeStart", hashChanged);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [anchor]);
return (
<Link href={to}>
<a ref={ref}>{children}</a>
</Link>
);
};
export default AnchorLink;
anchorlink.tsx
import { NextPage } from "next";
import AnchorLink from ".../anchorlink"
const Index: NextPage = () => (
<main>
<AnchorLink to={{hash: "hoge"}} anchor={"hoge"}>
<h1>Hoge</h1>
</AnchorLink>
</main>
)
export default Index;
特筆するべき点としては、Next RouterのhashChangeStart
イベントを監視しアンカーとURLに含まれる#以降の値がフルマッチした時のみscrollIntoView
でスクロールするようにしています。今回はLink
を内包したコンポーネントとして定義していますが、scrollIntoViewを使用できればa
タグ以外でも問題ないです。
他のテックブログでは外部ライブラリを紹介するような内容が散見されますが、このようにReact.jsとNext.jsのみで簡単に実装することができます。
Discussion