React Routerでハッシュリンクを実装する方法

Reactアプリケーションを開発する際、ルーティングライブラリとしてReact Routerを利用している人は多いでしょう。
React Routerは簡単に複数ページのルーティング設定ができ、非常に便利なライブラリです。

ただ、1つ不便な点があります。ハッシュリンクが効かないのです。
つまり、Reactアプリページ内の特定の要素への遷移ができません。

本記事では、React Routerを利用していてもハッシュリンクによる特定要素への遷移を実現する方法を紹介します。

結論

はじめに結論から言ってしまいます。

  • 現状(2023年11月時点)いい感じのパッケージは存在しない
  • カスタムフックを自作して設置すればいける!

それでは詳しく解説していきます。

使えそうなパッケージを調べる

やはり不便に思う人が多いのか、このReact Routerの弱点を克服するためのパッケージは複数公開されています。

全て見てみましたが、以下の理由で採択するにはどれも微妙、、という結論になりました。

  • react-router-hash-link
    現在の最新版であるReact Routerのversion 6に対応していない。
    実際にReact Router version 6を用いたアプリに導入してみると、パッケージの内部実装起因の型エラーが生じる。
  • react-router-hashlink
    これもReact Routerのversion 6に対応していない。
    というかスターも少ないし最終メンテが2017年だしさすがに使えないかな、、という感じ。(ちなみに上のパッケージと名前がとても紛らわしい。)
  • react-anchor-link-smooth-scroll
    最終メンテが2019年で古い。内部実装のReactのバージョンが15.6.2であることが物語っている。
  • react-scroll
    メンテも最近だしスターも多いし良さそう!と思って使ってみましたが、ページ内の遷移のみの対応で、ページを跨いだハッシュリンクによる遷移は機能しませんでした、、無念。

このように、イケてるパッケージがなかったため、自作してなんとかしてみることにしました。

ハッシュリンクを機能させるコードを紹介

まず、カスタムフックを作成します。

import { useCallback } from 'react';
import { useLocation } from 'react-router-dom';

type ScrollToAnchorProps = {
  smooth?: boolean;
};

export const useScrollToAnchor = ({
  smooth,
}: ScrollToAnchorProps): { scrollToAnchor: () => void } => {
  const location = useLocation();

  const scrollToAnchor = useCallback<() => void>(() => {
    const hash = location.hash.slice(1);
    const target = document.getElementById(hash);

    if (target) {
      setTimeout(() => {
        target.scrollIntoView({ behavior: smooth ? 'smooth' : 'auto', block: 'start' });
      }, 100);
    }
  }, [location, smooth]);

  return { scrollToAnchor };
};

このフックでは、遷移先URLに”#”が含まれていた場合、”#”以下の文字のidを持つ要素へ移動するよう定義しています。

また、propsとしてsmooth: booleanをとります。このsmoothtrueを指定するとスムーススクロールで移動させることができます。

このように作成したカスタムフックuseScrollToAnchorAppコンポーネント内で呼び出すことで、アプリ内のすべてのハッシュリンクが機能するようになります。

import { Suspense, useEffect } from 'react';
import  { useScrollToAnchor }  from "./useScrollToAnchor";
import Header from "./Header";
import Content from "./Content";

export const App = (): React.ReactElement => {
  const { scrollToAnchor } = useScrollToAnchor({ smooth: true });

  useEffect(() => {
    scrollToAnchor();
  }, [scrollToAnchor]);

  return (
    <div>
      <Header />
      <Content />
    </div>
  );
};

export default App;

まとめ

Reactアプリ開発者の多くが悩むであろう、React Routerバージョン6でハッシュリンクを機能させる方法を紹介しました。

現状うまく動くパッケージが存在しないため、カスタムフックを実装することで解決できました。
この方法ですと、パッケージのインストールや管理、バージョン管理も減らせますし、一石二鳥だと思います!

ぜひ参考にしてみてください。

コメント

タイトルとURLをコピーしました