Apps

014-React Hooks|Step3-2_useRefの使い方:ボタンクリック時に画像をスクロール

はじめに

React では、useRef を使用すると DOM 要素の参照を取得し、操作 することができます。

本記事では、useRef を活用して ボタンクリック時に画像リストをスクロールする 方法を解説します。

ボタンをクリックすると指定の画像までスムーズにスクロールする機能を実装し、useRef の実用的な使い方を学びましょう!

useRefとは

useRef は React のフックの一つで、コンポーネントの再レンダリングを発生させずに値を保持できる 仕組みです。主に次のような用途に使われます。

  • DOM 要素の参照(今回のケース)
  • 前回の値の保持
  • 状態を変更せずに変数を保持

通常、DOM を操作する場合は document.getElementById などのメソッドを使いますが、React では useRef を利用することで React の再レンダリングを管理しつつ、適切に DOM を操作 できます。

概要

今回の実装では、以下の要素を作成します。

  1. 画像リストを横スクロール可能にする
  2. ボタンをクリックすると、指定の画像までスムーズにスクロールする
  3. useRef を使って画像リストの ul 要素を参照し、スクロール処理を実行する

ボタンクリック時に scrollIntoView() を使って指定の画像を中央に表示 させる仕組みを実装します。

実装準備:画像を用意する

https://huggingface.co/spaces/stabilityai/stable-diffusion-3.5-large-turbo

Stable DiffusionをHugging Faceで使用して椅子の画像を生成します。

プロンプトは「chair」「good chair」「long chair」「low chair」「high chair」などにしました。(これは適当で大丈夫です)

実装:手順とコードの解説

ここでは、useRef を活用して ボタンクリック時に画像をスムーズにスクロール する仕組みを解説します。
手順ごとにコードを確認しながら、useRef の使い方を理解していきましょう!

1. useRef を使ってリスト全体の要素を取得

まず、スクロールさせる対象(画像のコンテナ)を参照するために、useRef を作成します。

const listRef: RefObject<HTMLUListElement> = useRef<HTMLUListElement>(null);
  • listRef画像の親要素(リスト全体)に適用 することで、その要素を取得できるようにします。
  • useRef はレンダリングの影響を受けず、直接 DOM を参照 できます。

リスト要素に ref={listRef} をセットすることで、コンポーネント内で listRef.current を使って親要素を操作できます。

2. スクロール処理を実装

ボタンクリック時に該当の画像にスクロールさせる scrollToIndex 関数を作成します。

const scrollToIndex = (index) => {
  console.log(listRef.current); // listRefの要素を確認
  const listNode = listRef.current; // 親要素の取得
  const imgNode = listNode?.querySelectorAll("div > img")[index]; // 指定した画像を取得
  console.log(imgNode);

  imgNode?.scrollIntoView({
    behavior: "smooth",
    block: "nearest",
    inline: "center",
  });
};

scrollToIndex のポイント

  1. listRef.current から画像の親要素(div)を取得
  2. querySelectorAll("div > img")画像のリストを取得
  3. index 番目の画像を取得
  4. scrollIntoView() を使ってスムーズにスクロール

3. ボタンクリック時に scrollToIndex を実行

次に、ボタンをクリックすると scrollToIndex が実行されるようにします。

<ButtonGroup variant="contained" aria-label="Basic button group">
  <Button onClick={() => scrollToIndex(0)}>Chair-1</Button>
  <Button onClick={() => scrollToIndex(1)}>Chair-2</Button>
  <Button onClick={() => scrollToIndex(2)}>Chair-3</Button>
  <Button onClick={() => scrollToIndex(3)}>Chair-4</Button>
  <Button onClick={() => scrollToIndex(4)}>Chair-5</Button>
</ButtonGroup>
  • クリックされたボタンの インデックスを scrollToIndex に渡す
  • scrollToIndex(0)1枚目の画像にスクロール
  • scrollToIndex(1)2枚目の画像にスクロール
  • scrollToIndex(4)5枚目の画像にスクロール

4. 画像のリストを作成

最後に、横スクロールできる画像リストを作成し、listRef を適用します。

<div style={{
  overflowX: "auto",
  maxWidth: "1200px",
  margin: "auto"
}}>
  <div
    className='flex items-center justify-between'
    style={{ minWidth: "2600px" }}
    ref={listRef} // ✅ ここに listRef を適用
  ><br />
    <Image src="/page3-2/image/chair-1.webp" alt='Chair-1' width={imgWidth} height={imgHight} />
    <Image src="/page3-2/image/chair-2.webp" alt='Chair-2' width={imgWidth} height={imgHight} />
    <Image src="/page3-2/image/chair-3.webp" alt='Chair-3' width={imgWidth} height={imgHight} />
    <Image src="/page3-2/image/chair-4.webp" alt='Chair-4' width={imgWidth} height={imgHight} />
    <Image src="/page3-2/image/chair-5.webp" alt='Chair-5' width={imgWidth} height={imgHight} />
  </div>
</div>

listRef の適用ポイント

  • 横スクロール可能な divlistRef を適用
  • これにより useRef を使って div の中の img 要素を取得し、スクロールできるようになります。

実装手順:まとめ

手順実装内容
1useRef を使って親要素(画像リスト)を取得
2scrollToIndex 関数を作成し、画像をスムーズにスクロール
3ボタンをクリックすると scrollToIndex が実行されるように設定
4画像リストに ref={listRef} を適用し、DOM を操作できるようにする

useRef を使うことで ボタンを押すだけで指定した画像にスムーズにスクロール できるようになりました!
次のセクションでは 最終コードをまとめて確認 しましょう 🚀

最終コード

"use client";
import * as React from 'react';
import Typography from '@mui/material/Typography';
import { Box, Button, ButtonGroup } from '@mui/material';
import { useRef } from 'react';
import Image from 'next/image';

export default function Page3_2() {
  const listRef: RefObject<HTMLUListElement> = useRef<HTMLUListElement>(null);
  
  const imgWidth = 500;
  const imgHight = 500;
  
  const scrollToIndex = (index) => {
    const listNode = listRef.current;
    const imgNode = listNode?.querySelectorAll("div > img")[index];
    imgNode?.scrollIntoView({
      behavior: "smooth",
      block: "nearest",
      inline: "center",
    });
  };

  return (
    <div>
      <Typography>Welcome to the Toolpad page3!</Typography><br />
      <ButtonGroup variant="contained" aria-label="Basic button group">
        <Button onClick={() => scrollToIndex(0)}>Chair-1</Button>
        <Button onClick={() => scrollToIndex(1)}>Chair-2</Button>
        <Button onClick={() => scrollToIndex(2)}>Chair-3</Button>
        <Button onClick={() => scrollToIndex(3)}>Chair-4</Button>
        <Button onClick={() => scrollToIndex(4)}>Chair-5</Button>
      </ButtonGroup>
      <div style={{ overflowX: "auto", maxWidth: "1200px", margin: "auto" }}>
        <div className='flex items-center justify-between' style={{ minWidth: "2600px" }} ref={listRef}><br />
          <Image src="/page3-2/image/chair-1.webp" alt='Chair-1' width={imgWidth} height={imgHight} />
          <Image src="/page3-2/image/chair-2.webp" alt='Chair-2' width={imgWidth} height={imgHight} />
          <Image src="/page3-2/image/chair-3.webp" alt='Chair-3' width={imgWidth} height={imgHight} />
          <Image src="/page3-2/image/chair-4.webp" alt='Chair-4' width={imgWidth} height={imgHight} />
          <Image src="/page3-2/image/chair-5.webp" alt='Chair-5' width={imgWidth} height={imgHight} />
        </div>
      </div>
    </div>
  );
}

useRef を使うメリット

React でスクロールや DOM の直接操作を行う際、useRef を使うと以下のようなメリットがあります。

① 再レンダリングを引き起こさない

通常、useState を使って値を管理すると、状態が変わるたびに コンポーネントが再レンダリング されます。しかし、useRef値を変更しても再レンダリングされない ため、パフォーマンスの向上につながります。

今回の実装でも useRef を使用することで、

  • 画像リストのスクロール位置を変更しても、コンポーネントが再レンダリングされない
  • パフォーマンスが向上する

といったメリットがあります。

② 直接 DOM を操作できる

React では基本的に 仮想 DOM(Virtual DOM)を介して UI を更新 しますが、特定のケースでは 直接 DOM を操作したい 場面もあります。

例えば、今回のような スクロールの制御フォーカスの設定 などは、useRef を使うことで直接 DOM を取得・変更できます。

今回の実装では、listRef.current を利用して

  • 画像のリストを取得
  • クリック時に scrollIntoView() でスムーズスクロール

という処理を行いました。

③ ボタンクリックごとに最新の値を保持できる

useRef を使うと、コンポーネントのライフサイクルを通して 最新の値を保持 できます。
この特徴を活かすことで、今回のように ボタンを押すたびに異なる画像にスクロール する処理が簡単に実装できます。

まとめ

本記事では、useRef を使って ボタンクリック時に画像をスムーズにスクロール する方法を解説しました。

この記事で学んだこと

  • useRef を使うことで、 特定の DOM 要素を参照 できる
  • useRef値が変化しても再レンダリングされない
  • scrollIntoView() を活用して、スムーズにスクロールできる
  • useRef を使うことで、パフォーマンスの最適化につながる

今回の実装を応用すると、カルーセル(スライダー)スクロールナビゲーション など、さまざまな UI に活用できます。
ぜひ useRef を活用して、よりスムーズなユーザー体験を実現してみてください! 🚀

-Apps