Apps

014-React Hooks|Step3-4_useRefの使い方:forwardRefで動画を再生・停止・リセットする

はじめに

React で動画を操作する際、useRefforwardRef を活用すると、コンポーネントをまたいで ref を渡し、動画の再生・停止・リセットをスムーズに制御できます。

本記事では、useRefforwardRef を使用して、ボタンで動画の制御を行う方法を解説します。

この実装により、React の再レンダリングを抑えつつ、ユーザー操作による動画の管理が簡単にできるようになります。

動画プレイヤーをカスタマイズしたい場合や、再生状態をプログラムで制御したい場合に役立つテクニックです。

概要:forwardRefで別のコンポーネントのDOMノードにアクセス

自分で作ったコンポーネントに対してどうやってuseRefを関連付けることができるか

動画を再生・一時停止・リセットするシンプルな 動画プレイヤー を作成しています。

Page3_4.tsx でボタンを配置し、VideoPlayer.tsx で動画コンポーネントを定義しています。

実装準備:ページと動画を用意する

1. ページを作成

page.tsx

// Page3_4.tsx
"use client";
import * as React from "react";
import Typography from "@mui/material/Typography";
import { Button } from "@mui/material";
import { VideoPlayer } from "./VideoPlayer";

export default function Page3_4() {
  // YouTubeの埋め込み用URLに変換
  const videoSrc = "https://www.youtube.com/embed/7rka9b5wLmU";

  return (
    <div>
      <Typography>Welcome to the Toolpad page3-4!</Typography>
      <br />
      <br />
      <Button variant="outlined">Play</Button>
      <Button variant="outlined">Pause</Button>
      <Button variant="outlined">Reset</Button>
      <br />
      <br />
      <VideoPlayer src={videoSrc} width="560" height="315" />
    </div>
  );
}

page3-4/VideoPlayer.tsx

// VideoPlayer.tsx
import { Card, CardContent, CardMedia } from '@mui/material';
import Typography from '@mui/material/Typography';

type VideoPlayerProps = {
  width: string;
  height: string;
  src: string;
};

export const VideoPlayer = ({ width, height, src }: VideoPlayerProps) => {
  return (
    <div>
      <Typography>YouTube video player</Typography>
      <br />
      <Card sx={{ maxWidth: 600 }}>
        <CardMedia
          component="video"
          src="/page3-4/video/Zaha Hadid_Moon System 2007.mp4"
          controls
        />
        <CardContent>
          <Typography variant="h6">Zaha Hadid Moon System 2007</Typography>
        </CardContent>
      </Card>
    </div>
  );
};

2. 動画を用意

今回は以前にiPadのお絵描きアプリでタイムラプスを使って作成したZaha HadidのMoon Systemを使用します。

自前の適当な動画がない方は以下からダウンロードしてください。

実装:手順と解説

1. VideoPlayer コンポーネントの作成 (VideoPlayer.tsx)

VideoPlayer.tsx では、動画を表示する 再利用可能なコンポーネント を作成します。

コードのポイント

  1. forwardRef を使い、親コンポーネントから <video> を操作できるようにする
  2. CardMediacomponent="video" で MUI の Card に動画を埋め込む
  3. ref<video> に適用 し、親コンポーネント (Page3_4.tsx) から play() / pause() / currentTime を操作可能にする。

VideoPlayer.tsx の流れ

  1. Props を受け取る (width, height, src)
  2. forwardRef を使い、ref<video> に適用
  3. MUI の Card を使ってデザインを整える
import { Card, CardContent, CardMedia } from '@mui/material';
import Typography from '@mui/material/Typography';
import { forwardRef } from 'react';

type VideoPlayerProps = {
  width: string;
  height: string;
  src: string;
};

export const VideoPlayer = forwardRef<HTMLVideoElement, VideoPlayerProps>(
  ({ width, height, src }, ref) => {
    return (
      <div>
        <Typography>Video player</Typography>
        <br />
        <Card sx={{ maxWidth: 600 }}>
          <CardMedia
            component="video"
            src={src}
            controls
            sx={{ width, height }}
            ref={ref} // ✅ ref を <video> に適用 (親コンポーネントから操作可能に)
          />
          <CardContent>
            <Typography variant="h6">Zaha Hadid Moon System 2007</Typography>
          </CardContent>
        </Card>
      </div>
    );
  }
);

VideoPlayer.displayName = "VideoPlayer"; // ✅ 名前を設定 (デバッグ用)

2. Page3_4.tsx で動画プレイヤーを配置

このコンポーネントでは、ボタンで 動画を再生・一時停止・リセット する機能を実装します。

コードのポイント

  1. useRef<HTMLVideoElement> を作成し、VideoPlayer<video> を参照
  2. ボタン (Button) を使って、動画の play() / pause() / currentTime を制御
  3. VideoPlayerref を渡し、親コンポーネントから制御

Page3_4.tsx の流れ

  1. videoRef を作成し、<video> を参照
  2. ボタンで動画を操作
  3. VideoPlayerref を渡して動画を表示
"use client";
import * as React from "react";
import Typography from "@mui/material/Typography";
import { Button } from "@mui/material";
import { VideoPlayer } from "./VideoPlayer";

export default function Page3_4() {
  // ✅ 動画要素を参照する useRef
  const videoRef = React.useRef<HTMLVideoElement>(null);

  const videoSrc = "/page3-4/video/Zaha Hadid_Moon System 2007.mp4";

  // ✅ 最初に戻す処理
  const handleReset = () => {
    if (videoRef.current) {
      videoRef.current.currentTime = 0; // ⬅️ 再生位置を 0 にリセット
      videoRef.current.pause(); // ⬅️ 停止状態にする
    }
  };

  return (
    <div>
      <Typography>Welcome to the Toolpad page3-4!</Typography>
      <br />
      <br />
      {/* ✅ ボタンで動画を操作 */}
      <Button onClick={() => videoRef.current?.play()} variant="outlined">
        Play
      </Button>
      <Button onClick={() => videoRef.current?.pause()} variant="outlined">
        Pause
      </Button>
      <Button onClick={handleReset} variant="outlined">
        Reset
      </Button>
      <br />
      <br />
      {/* ✅ VideoPlayer を表示 (ref を渡す) */}
      <VideoPlayer ref={videoRef} src={videoSrc} width="560" height="315" />
    </div>
  );
}

3. コンポーネント間の関係

ファイル役割
VideoPlayer.tsx動画プレイヤー本体 (forwardRef を使用)
Page3_4.tsxボタンで動画を制御する親コンポーネント (useRef<video> を操作)
  1. Page3_4.tsxuseRef<HTMLVideoElement> を作成
  2. VideoPlayer.tsxref を渡し、 を操作可能に
  3. ボタン (Button) で動画を制御

4. 動作確認

  1. ブラウザでページを開く
  2. ボタンをクリック
    • Play → 動画が再生される
    • Pause → 動画が一時停止する
    • Reset → 動画が最初に戻って停止する
  3. コンソールにエラーがないか確認

実装手順:まとめ

手順内容
1VideoPlayer.tsxforwardRef を使って <video> を参照可能にする
2Page3_4.tsxuseRef を作成し、VideoPlayerref を渡す
3Buttonplay() / pause() / currentTime = 0 を操作
4動作確認 (Play / Pause / Reset ボタンをチェック)

このように React の forwardRef + useRef を活用すると、親コンポーネントから子コンポーネントの要素を操作できます!

最終コード

page.tsx

// Page3_4.tsx
"use client";
import * as React from "react";
import Typography from "@mui/material/Typography";
import { Button } from "@mui/material";
import { VideoPlayer } from "./VideoPlayer";

export default function Page3_4() {

  const videoRef = React.useRef<HTMLVideoElement>(null);

  const videoSrc = "/page3-4/video/Zaha Hadid_Moon System 2007.mp4";

  const handleReset = () => {
    if (videoRef.current) {
      videoRef.current.currentTime = 0; // 最初に戻る
      videoRef.current.pause(); // 停止状態にする
    }
  }

  return (
    <div>
      <Typography>Welcome to the Toolpad page3-4!</Typography>
      <br />
      <br />
      <Button onClick={() => videoRef.current?.play()} variant="outlined">Play</Button>
      <Button onClick={() => videoRef.current?.pause()} variant="outlined">Pause</Button>
      <Button
        onClick={handleReset}
        variant="outlined"
      >
        Reset
      </Button>
      <br />
      <br />
      <VideoPlayer ref={videoRef} src={videoSrc} width="560" height="315"/>
    </div>
  );
}

VideoPlayer.tsx

// VideoPlayer.tsx
import { Card, CardContent, CardMedia } from '@mui/material';
import Typography from '@mui/material/Typography';
import { forwardRef } from 'react';

type VideoPlayerProps = {
  width: string;
  height: string;
  src: string;
};

export const VideoPlayer = forwardRef<HTMLVideoElement, VideoPlayerProps>(
  ({ width, height, src }, ref) => {
    return (
      <div>
        <Typography>Video player</Typography>
        <br />
        <Card sx={{ maxWidth: 600 }}>
          <CardMedia
            component="video"
            src={src}
            controls
            sx={{ width, height }}
            ref={ref} // ref を <video> に適用
          />
          <CardContent>
            <Typography variant="h6">Zaha Hadid Moon System 2007</Typography>
          </CardContent>
        </Card>
      </div>
    );
  }
);

VideoPlayer.displayName = "VideoPlayer";

まとめ

useRefforwardRef を活用することで、React コンポーネント間で ref を受け渡し、動画の操作を簡単に実装できました。本記事のポイントは以下の通りです。

  • useRef を使用すると、コンポーネントの再レンダリングを抑えつつ、DOM 要素への参照を保持できる
  • forwardRef を利用することで、子コンポーネント (VideoPlayer) へ ref を渡せる
  • refvideo 要素に適用し、play()pause()currentTime を操作することで、ボタンで動画をコントロールできる

この方法を応用すれば、動画プレイヤーのカスタマイズや、アニメーション・スライドショーの制御など、さまざまな UI の実装が可能になります。ぜひ、自分のプロジェクトにも取り入れてみてください!

-Apps