【React x TypeScript】useScroll を作る


こんにちは Gaji-Labo フロントエンドエンジニアの茶木です。

ページのTOPに戻るを始め、入力フォームのバリデーションでエラーがあった箇所に移動するといった、ページ内移動が必要なケースときどきありますよね。Reactではこれはカスタムフックとして準備しておくと便利です。

定義部

import { ReactElement, RefObject, useCallback, useRef } from "react";

export const useScroll = (): [RefObject<HTMLDivElement>, () => void] => {
  const ref = useRef<HTMLDivElement>(null);
  const moveTo = useCallback(() => {
    ref?.current?.scrollIntoView({
      behavior: "smooth",
    });
  }, []);
  return [ref, moveTo];
};

呼び出し部

export const App = (): ReactElement | null => {
  const [ref, moveTo] = useScroll();
  return (
    <div>
      <button type="button" onClick={moveTo}>
        移動
      </button>
      <div style={{ height: "200vh" }}>内容</div>
      <h2 ref={ref}>footer</h2>
    </div>
  );
}

解説

ボタンを押すと footerへ移動します。

scrollIntoView

https://developer.mozilla.org/ja/docs/Web/API/Element/scrollIntoView

scrollIntroView は、IE 未対応、Safari は scrollIntroView のオプションが機能しません。特にオプションはスムーズスクロールが機能しないので、アニメーションを見せたい場合は自前で書く必要があります。

自前でアニメーションする

const scrollAnimation = (restStep: number, destination: number) => {
  const delta = (destination - window.pageYOffset) / restStep;
  window.scrollBy(0, delta);
  const nextRestStep = restStep - 1;
  if (!nextRestStep) return;
  window.requestAnimationFrame(() =>
    scrollAnimation(nextRestStep, destination)
  );
};
  const moveTo = useCallback(() => {
    const top = ref?.current?.getBoundingClientRect()?.top;
    if (top === undefined) return;
    scrollAnimation(30, top + window.pageYOffset);
  }, []);
  return [ref, moveTo];

要素の位置、スクロール位置を window.requestAnimationFrame で更新しています。

Gaji-Laboでは、React経験が豊富なフロントエンドエンジニアを募集しています

弊社ではReactの知見で事業作りに貢献したいフロントエンドエンジニアを募集しています。大きな制作会社や事業会社とはひと味もふた味も違うGaji-Laboを味わいに来ませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください。お仕事お問い合わせや採用への応募、共に大歓迎です!

求人応募してみる!

投稿者 Chaki Hironori

webライターもやってるフロントエンドエンジニアです。Reactは自信があります。またデザイン畑の出身で、気持ちのいいアニメーションやインタラクティブな表現は丁寧に手掛けます。好きなものは中南米の遺跡で、スペイン語が少しできます。