Skip to content

ReactのuseRefでページ内のスムーススクロールを実装する【複数要素対応】

Reactでページ内のスムーススクロールおよび複数の項目から該当する各セクションへのスクロールをライブラリなしで実装する方法です。

Reactでのページ内のスムーススクロールは refscrollIntoView によりライブラリなしで実装できますが、 ランディングページのように長いページ内に複数セクションがあり、各メニュー項目をクリックした際に該当セクションまでスムーススクロールさせたい場合も同じ方法で実装できます。

ページの最後にデモページへのリンクがあります。

ref と scrollIntoView を使用したスムーススクロールの例

useRefref を宣言して、スクロールの対象となる要素に設定します。

const targetRef = useRef<HTMLDivElement | null>(null);

ターゲット要素

<div ref={targetRef} />

クリックした際の処理 handleClick を作成して、スクロール発火のトリガーとなるクリック要素(buttonなど)の onClick に設定します。

const handleClick = () => {
  if (!targetRef.current) return;
  targetRef.current.scrollIntoView({ behavior: "smooth" });
};

トリガー要素(buttonなど)

<button className="gototop" type="button" onClick={handleClick}>
  Click
</button>

スムーススクロール デモ(CodePen)

複数要素へのスムーススクロールの例

複数要素の場合、ターゲットに設定するrefを要素ごとに作成する必要があります。

次のように書いてしまうとセクションが多くなればなるほど記述が増えてしまうため、 map() を使用します。

// 🙅‍♀️
const targetRef1 = useRef<HTMLDivElement | null>(null);
const targetRef2 = useRef<HTMLDivElement | null>(null);
const targetRef3 = useRef<HTMLDivElement | null>(null);

map() を使用して ref を持った配列を作成します。

const sectionNames: SectionProps[] = [
  {
    name: "Section 1",
  },
  {
    name: "Section 2",
  },
  {
    name: "Section 3",
  },
];

const sections = sectionNames.map((name) => {
  return {
    ...name,
    ref: useRef<HTMLDivElement>(),
  };
});

作成した sections 配列からターゲットとなるセクションととトリガー要素を作成します。
その際にセクションには ref を付与します。

/* トリガー要素 */
<nav className="nav">
  {sections.map((section) => (
    <a className="navItem" key={section.name} onClick={handleClick}>
      {section.name}
    </a>
  ))}
</nav>;

/* セクション */
{
  sections.map((section) => (
    <section className="container" key={section.name} ref={section.ref}>
      {section.name}
    </section>
  ));
}

次に handleClick を作成します。 1つの要素の場合と違う点は handleClick に対象となる ref をpropsとして渡すことで、その ref に対して scrollIntoView() が実行されるようにします。

const handleClick = (ref) => {
  if (!ref.current) return;
  ref.current.scrollIntoView({ behavior: "smooth" });
};

先ほど作成したトリガー要素の onClick を次のように変更します。

onClick={() => handleClick(section.ref)}

複数要素に対応したページ内のスムーススクロールは完成です 🎉

複数要素のスムーススクロール デモ(CodePen)

参照