はじめに
先日、noteで 「YouTube連動のポモドーロタイマーアプリ『TubePomo』」 の開発ストーリーと概要を紹介しました。
前回の記事では、アプリのコンセプトや開発の背景、そして「こだわった実装ポイント」として
- YouTubeのURLから動画・再生リストIDを抽出する処理
- 設定をLocalStorageと同期するカスタムフック
の2点を簡単に紹介し、「技術的な詳細は別記事で」とお伝えしていました。今回はその続編として、TubePomoのコンポーネント構成をもう少し技術寄りに掘り下げてまとめます。
アプリ概要
YouTubeの音楽と連動して動くポモドーロタイマーアプリ「TubePomo」を作りました。集中時間と休憩時間でそれぞれ別のプレイリストを再生できるのが特徴です。
- 公開URL: https://takato0718.github.io/pomodoro/
- 技術スタック: React(Vite) / Tailwind CSS v4 / react-youtube / LocalStorage
今回は、このアプリのコンポーネント構成を簡単にまとめてみます。
コンポーネント構成
見やすさのため、「①画面コンポーネントの階層」と「②hooks・utilsとの依存関係」を、どちらも箇条書き・表で掲載します。
①コンポーネントの階層
App.jsx を頂点に、各画面パーツがどうぶら下がっているかを示すと以下のようになります。
-
App.jsx(状態管理の中心)Timer.jsx-
Player.jsxPlayerSizeControls.jsx
PlaylistForm.jsxTrackList.jsx
App.jsx がタイマー設定・曲リスト・再生位置などすべての状態を持つ中心的な存在で、各コンポーネントはそこから props を受け取って表示・操作する役割に徹しています。PlayerSizeControls.jsx だけは Player.jsx の子コンポーネントとして、プレイヤーの表示サイズ切り替えを担当しています。
②hooks・utilsとの依存関係
各コンポーネントがどの hooks / utils を使っているかを、表にまとめました。
| 呼び出し元 | 使用している hooks / utils |
|---|---|
App.jsx |
useTimer.js、useLocalStorage.js、constants.js、playlist.js、timerSettings.js、youtubeErrors.js
|
Timer.jsx |
formatTime.js |
Player.jsx |
playerLayout.js |
PlaylistForm.jsx |
youtube.js |
TrackList.jsx |
youtube.js |
各ファイルの役割 簡易まとめ
components(画面に表示されるパーツ)
| ファイル | 役割 |
|---|---|
Timer.jsx |
集中/休憩の残り時間表示、スタート・一時停止・リセット、分数設定の入力欄 |
Player.jsx |
YouTube IFrame Player(react-youtube)のラッパー。再生/一時停止・音量・サイズ切り替え・エラー通知を担当 |
PlayerSizeControls.jsx |
プレイヤー表示サイズ(小・中・大)を切り替える3ボタンのセグメントコントロール |
PlaylistForm.jsx |
YouTubeのURLを入力して曲・プレイリストを追加するフォーム |
TrackList.jsx |
登録済みの曲一覧の表示、削除、並び替え(Top/↑/↓/Bottom)UI |
hooks(状態ロジックの分離)
| ファイル | 役割 |
|---|---|
useTimer.js |
集中/休憩モードのカウントダウンと切り替えだけを管理するカスタムフック。YouTube側のことは一切知らない |
useLocalStorage.js |
useState と LocalStorage を同期させる汎用フック。設定や曲リストの永続化に使用 |
utils(純粋関数・定数)
| ファイル | 役割 |
|---|---|
constants.js |
LocalStorageのキー、デフォルト設定、タイマーの分数の上限/下限、プレイヤーサイズなどの定数をまとめる場所 |
playlist.js |
曲リストから再生対象を取り出す・並び替えるなどの配列操作関数 |
playerLayout.js |
画面サイズから、プレイヤーの表示サイズ(小・中・大)ごとの幅・高さを計算する |
youtubeErrors.js |
YouTube Player のエラーコードをメッセージに変換し、再生エラー時のリトライ上限を算出する |
youtube.js |
YouTubeのURLから動画IDやプレイリストIDを抽出・検証する |
formatTime.js |
秒数を MM:SS 形式の文字列に変換する |
timerSettings.js |
タイマーの分数設定を1〜90分の範囲に収める |
設計で意識したこと
コンポーネント構成を考える上で、次の3点を意識しました。
-
状態は
App.jsxに集約する
タイマーの状態や曲リストなど、複数のコンポーネントから参照される情報はApp.jsxにまとめ、各コンポーネントは props 経由で受け取って表示・操作するだけにしています。状態がどこにあるか迷わないようにするためです。 -
hooks でロジックを分離する
useTimerはタイマーのカウントダウンだけ、useLocalStorageは永続化だけ、というように役割を1つに絞ったカスタムフックとして切り出しています。前回のnote記事で紹介した「設定をLocalStorageに保存する処理」も、このuseLocalStorage.jsとして実装しています。 -
utils は副作用のない純粋関数にする
youtube.js(URL解析)やformatTime.js(時間のフォーマット)など、入力に対して同じ出力を返すだけの関数として切り出すことで、テストや再利用がしやすい形にしています。
まとめ
-
状態は
App.jsxに集約し、各コンポーネントは props 経由で受け取って表示するだけにする -
hooks でロジックを分離(
useTimerはタイマー、useLocalStorageは永続化)することで、コンポーネント側をシンプルに保つ - utils は副作用のない純粋関数として切り出し、テストや再利用がしやすい形にする
役割ごとにファイルを分けておくと、後から機能を追加するときにも「どこを触ればいいか」がすぐわかるので、個人開発でも保守しやすいと感じました。
アプリ自体は下記から無料で試せますので、よければ触ってみてください。フィードバックもお待ちしています。
🔗 https://takato0718.github.io/pomodoro/