はじめに
React では、useRef を使用すると DOM 要素の参照を取得し、操作 することができます。
本記事では、useRef を活用して ボタンクリック時に画像リストをスクロールする 方法を解説します。
ボタンをクリックすると指定の画像までスムーズにスクロールする機能を実装し、useRef の実用的な使い方を学びましょう!
useRefとは
useRef は React のフックの一つで、コンポーネントの再レンダリングを発生させずに値を保持できる 仕組みです。主に次のような用途に使われます。
- DOM 要素の参照(今回のケース)
- 前回の値の保持
- 状態を変更せずに変数を保持
通常、DOM を操作する場合は document.getElementById などのメソッドを使いますが、React では useRef を利用することで React の再レンダリングを管理しつつ、適切に DOM を操作 できます。
概要
今回の実装では、以下の要素を作成します。
- 画像リストを横スクロール可能にする
- ボタンをクリックすると、指定の画像までスムーズにスクロールする
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 のポイント
listRef.currentから画像の親要素(div)を取得querySelectorAll("div > img")で 画像のリストを取得index番目の画像を取得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 の適用ポイント
- 横スクロール可能な
divにlistRefを適用 - これにより
useRefを使ってdivの中のimg要素を取得し、スクロールできるようになります。
実装手順:まとめ
| 手順 | 実装内容 |
|---|---|
| 1 | useRef を使って親要素(画像リスト)を取得 |
| 2 | scrollToIndex 関数を作成し、画像をスムーズにスクロール |
| 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 を活用して、よりスムーズなユーザー体験を実現してみてください! 🚀
