Closed10

[勉強メモ] Reactを使い「音楽プレイヤー」を作ります

sousquaredsousquared

tailwindcss

環境構築しなくても試せるサイト
https://play.tailwindcss.com/

tailwindcssを使うと、cssをほとんど書かなくていい

インストール

yarn add -D tailwindcss

yarn add -D tailwindcss コマンドの -D は、--dev の短縮形です。これは、パッケージを開発依存関係(devDependencies)としてインストールするためのオプションです。
具体的には、tailwindcss をプロジェクトの開発時に必要なパッケージとしてインストールし、プロダクション環境でのビルドや本番稼働時には含めないようにします。開発中にのみ必要なツールやライブラリ(例えば、テストフレームワークやビルドツールなど)は、devDependencies に含めるのが一般的です。

tailwind.config.jsとindex.cssでのimport

https://tailwindcss.com/docs/guides/create-react-app

sousquaredsousquared

環境変数

.env に設定する (gitignoreすることを忘れない)

REACT_APP_ のprefixが必要

process.env.{環境変数名} でアクセス可能

sousquaredsousquared

array, fill, map

        {Array(20)
          .fill()
          .map(() => {
            return (
              <div className="flex-none cursor-pointer ">
                <img
                  alt="thumbnail"
                  src={
                    'https://i.scdn.co/image/ab67616d0000b2738b7a8c1322028d45a8355f7a'
                  }
                  className="mb-2 rounded"
                />
                <h3 className="text-lg font-semibold">Song Name</h3>
                <p className="text-gray-400">By Artist</p>
              </div>
            );
          })}

1. Array(20)

まず、Array(20) は長さが20の配列を作成します。しかし、この配列は要素が「空」の状態、つまり各インデックスに値が設定されていない状態です。このとき、配列の内容は以下のようになります:

[empty × 20]

このような「空のスロット」は、JavaScriptにおいては未定義の値 undefined が入っているわけではなく、要素そのものが存在しない状態です。

2. .fill()

次に .fill() メソッドを使用します。.fill() は配列の全ての要素を指定した値で埋めるメソッドです。引数を指定しない場合、デフォルトで undefined が使用されます。

Array(20).fill()

これにより、配列の全ての要素が明示的に undefined で埋められます。この状態の配列は以下のようになります:

[undefined, undefined, ..., undefined] // 長さ20

.fill() を使用する理由は、配列のメソッドである .map().forEach() は要素が「存在する」場合にのみコールバック関数を適用するためです。要素が「空」のままだと、これらのメソッドはその要素を無視します。fill() を使って要素を埋めることで、全ての要素に対してコールバック関数を適用できるようになります。

3. .map(() => { ... })

最後に .map() メソッドを使用します。.map() は配列の各要素に対してコールバック関数を適用し、その結果から新しい配列を生成します。

Array(20)
  .fill()
  .map(() => {
    // コールバック関数の内容
  });

このコードでは、コールバック関数内で以下のようなReactのJSX要素を返しています:

return (
  <div className="flex-none cursor-pointer">
    <img
      alt="thumbnail"
      src={'https://i.scdn.co/image/ab67616d0000b2738b7a8c1322028d45a8355f7a'}
      className="mb-2 rounded"
    />
    <h3 className="text-lg font-semibold">Song Name</h3>
    <p className="text-gray-400">By Artist</p>
  </div>
);

つまり、配列の各要素(この場合は20個の undefined)に対して、このJSX要素が生成されます。その結果、20個の同一のコンポーネントが含まれた配列が作成されます。

まとめ

  • Array(20):長さ20の「空」の配列を作成します。
  • .fill():配列の全ての要素を undefined で埋め、要素が「存在する」状態にします。
  • .map(() => { ... }):各要素に対してコールバック関数を適用し、JSX要素(Reactコンポーネント)を生成します。

このコード全体の目的は、20個の同一のコンポーネントを効率的に生成することです。例えば、音楽アプリやウェブサイトで、一覧表示するアイテムが複数ある場合に、このような手法でUIを構築することができます。

補足:配列の「空のスロット」と「undefined の値が入った要素」の違い

  • 空のスロット(sparse array):要素そのものが存在しない状態。Array(20) で生成される配列はこの状態です。この状態では、map() や forEach() などのメソッドは要素を無視します。
  • undefined の値が入った要素:要素は存在し、その値が undefined になっている状態。Array(20).fill() で生成される配列はこの状態です。この状態では、map() や forEach() などのメソッドは要素を正しく処理します。

実際の使用例

このコードは、Reactを使用したコンポーネントのレンダリングにおいて、特定の数の同一コンポーネントを繰り返し表示したい場合に有用です。例えば、ダミーデータを表示してレイアウトを確認したい場合や、ローディング中にプレースホルダーとして表示する際に使われます。


注意点

  • キーの指定:Reactで配列をレンダリングする際には、各要素に一意の key プロパティを設定することが推奨されます。このコードでは key が指定されていないため、実際のアプリケーションではエラーや警告が発生する可能性があります。

    例:

    map((_, index) => {
      return (
        <div key={index} className="flex-none cursor-pointer">
          {/* ... */}
        </div>
      );
    });
    
    
  • パフォーマンスの最適化:大量の要素を一度にレンダリングすると、パフォーマンスに影響を与える可能性があります。必要に応じて、仮想スクロールやページネーションなどの手法を検討してください。

sousquaredsousquared

useRef

const audioRef = useRef(null);

const handleSongSelected = async (song) => {
  setSelectedSong(song);
  audioRef.current.src = song.preview_url;
  audioRef.current.play();
  setIsPlay(true);
};

1. useRef フックとは

useRef はReactのフックの一つで、ミューテーブルな(可変の)参照オブジェクトを作成します。このオブジェクトはコンポーネントのライフサイクル全体を通じて持続し、その間に値を保持または更新することができます。

const myRef = useRef(initialValue);
  • myRef は { current: initialValue } という形のオブジェクトを返します。
  • initialValue は参照の初期値です。

2. current プロパティ

useRef が返すオブジェクトには current というプロパティがあります。この current が実際の値を保持します。

myRef.current = newValue;
  • myRef.current は、必要に応じて読み書きできます。
  • Reactの再レンダー間で値を保持したい場合や、DOM要素への参照を保持したい場合に使用します。

3. DOM要素への参照

useRef は主に以下の2つの用途で使用されます:

  1. DOM要素への直接アクセス:特定のDOM要素を直接操作したい場合。
  2. ミューテーブルな値の保持:レンダリング間で持続するミューテーブルな値を保持したい場合。

DOM要素への参照の例

import { useRef } from 'react';

function MyComponent() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus(); // input要素にフォーカスを当てる
  };

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>フォーカス</button>
    </div>
  );
}
  • inputRef は useRef(null) によって初期化されています。
  • <input ref={inputRef} /> で、input 要素の参照を inputRef.current に割り当てます。
  • inputRef.current を通じて input 要素にアクセスし、focus() メソッドを呼び出しています。

4. ご提供のコードにおける current

ご提供いただいたコードを見てみましょう:

const audioRef = useRef(null);

const handleSongSelected = async (song) => {
  setSelectedSong(song);
  audioRef.current.src = song.preview_url;
  audioRef.current.play();
  setIsPlay(true);
};

解説

  1. audioRef の宣言

    const audioRef = useRef(null);
    
    • audioRef は初期値 null を持つ参照オブジェクトです。
    • つまり、audioRef は { current: null } というオブジェクトになります。
  2. audioRef を DOM 要素に結びつける

    コード上では示されていませんが、おそらくレンダリング部分で audio 要素に ref 属性を付けているはずです。

    <audio ref={audioRef} />
    
    • これにより、audioRef.current に audio 要素の参照が代入されます。
  3. audioRef.current の使用

    audioRef.current.src = song.preview_url;
    audioRef.current.play();
    
    • audioRef.current を通じて、audio 要素のプロパティやメソッドにアクセスしています。
    • src に曲のプレビューURLを設定し、play() メソッドで再生を開始しています。

5. まとめ

  • useRef:Reactのフックで、ミューテーブルな参照オブジェクト { current } を返します。
  • currentuseRef が返すオブジェクトのプロパティで、現在の参照先の値を保持します。
  • audioRef.currentaudio DOM要素への参照を保持しており、そのプロパティやメソッドにアクセスできます。
  • 用途current を使用して、再レンダー間で持続する値を保持したり、DOM要素を直接操作できます。

補足

  • useRef と再レンダー
    • useRef の current プロパティが更新されても、コンポーネントは再レンダーされません。
    • そのため、useState と異なり、値の更新が再レンダーをトリガーしない場合に適しています。
  • なぜ current なのか
    • useRef は単に値を保持するだけでなく、参照先の値が変化しても再レンダーを引き起こさない特性があります。
    • current プロパティはそのために存在し、ミューテーブルな値を保持します。

具体的な例

もしコード内で audio 要素を定義している部分が以下のようになっているとします:

return (
  <div>
    {/* 他のコンポーネントや要素 */}
    <audio ref={audioRef} />
    {/* 他のコンポーネントや要素 */}
  </div>
);
  • ここで、<audio ref={audioRef} /> により、audioRef.current には <audio> 要素の参照が格納されます。
  • その後、audioRef.current.src や audioRef.current.play() を用いて音声の操作が可能になります。

まとめるとcurrentuseRef フックが返すオブジェクトのプロパティであり、現在の参照先、つまり実際の値やDOM要素を指し示すものです。これを利用して、コンポーネント内で直接DOM要素を操作したり、ミューテーブルな値を保持することができます。

sousquaredsousquared

条件付きレンダリング

{selectedSong != null && <Player />}

このコードは、Reactコンポーネントのレンダー部分でよく用いられる条件付きレンダリングのパターンです。

意味と動作

  • selectedSong != nullselectedSong が null ではない、つまり何らかの値が設定されている場合をチェックしています。
  • &&:論理AND演算子です。左側の条件が true の場合にのみ、右側の値を評価・返します。
  • <Player />Player というReactコンポーネントをレンダリングします。

したがって、このコードは次のような意味になります:

  • selectedSong が null でなければ、<Player /> コンポーネントをレンダリングする。
  • selectedSong が null(または undefined)の場合、何もレンダリングしない。

別の書き方との比較

条件付きレンダリングは他の方法でも実現できます。例えば、三項演算子を使う方法です。

{selectedSong != null ? <Player /> : null}

しかし、論理AND演算子を使うパターンの方がコードが簡潔になるため、よく使用されます。

注意点

  • 厳密等価演算子の使用!= よりも !==(厳密不等価演算子)を使うことで、型の違いによる予期せぬ挙動を防ぐことができます。

    {selectedSong !== null && <Player />}
    
  • undefined の考慮selectedSongundefined になる可能性がある場合、nullundefined の両方を考慮するために、単に selectedSong を条件として使うこともできます。

    {selectedSong && <Player />}
    
    • この場合、selectedSong が null や undefinedfalse0''(空文字列)などの「falsy」な値であれば、<Player /> はレンダリングされません。
    • ただし、selectedSong が数値の 0 や空文字列の可能性がない場合、この方法で問題ありません。

応用例

以下は、このパターンを他の状況で適用する例です。

  • ユーザーの認証状態に応じた表示

    {isLoggedIn && <Dashboard />}
    
    • isLoggedIn が true の場合、<Dashboard /> コンポーネントを表示します。
  • エラーメッセージの表示

    {errorMessage && <div className="error">{errorMessage}</div>}
    
    • errorMessage が存在する場合、その内容を表示します。
sousquaredsousquared

play()メソッドについて

play() メソッドは、JavaScriptの標準的なWeb APIである HTMLMediaElement インターフェースから提供されるメソッドです。これは、<audio><video> 要素などのメディア要素に対して使用できます。

https://developer.mozilla.org/ja/docs/Web/API/HTMLMediaElement


具体的な説明

  1. audioRef の宣言

    const audioRef = useRef(null);
    
    • audioRef は初期値 null を持つ参照オブジェクトです。
    • useRef フックを使用して、コンポーネント内で <audio> 要素への参照を保持します。
  2. <audio> 要素への参照の設定

    レンダリング部分で <audio> 要素に ref 属性を設定しているはずです。

    return (
      <div>
        {/* その他のコンポーネントや要素 */}
        <audio ref={audioRef}></audio>
        {/* その他のコンポーネントや要素 */}
      </div>
    );
    
    • <audio> 要素に ref={audioRef} を設定することで、audioRef.current に <audio> 要素の DOM ノードが格納されます。
  3. play() メソッドの呼び出し

    const handleSongSelected = async (song) => {
      setSelectedSong(song);
      audioRef.current.src = song.preview_url; // 再生する音声のソースを設定
      audioRef.current.play(); // 再生を開始
      setIsPlay(true);
    };
    
    • audioRef.current は <audio> 要素を参照しています。
    • audioRef.current.play() により、音声の再生を開始します。

play() メソッドの出所

  • HTMLMediaElement.play()
    • play() は、ブラウザの組み込みオブジェクトである HTMLMediaElement インターフェースが提供するメソッドです。
    • <audio> 要素は HTMLAudioElement であり、これは HTMLMediaElement を継承しています。
    • play() メソッドはメディア(音声や動画)の再生を開始します。
    • このメソッドは 非同期 であり、Promise オブジェクトを返します。
  • 主なメディア要素とメソッド
    • <audio> および <video> 要素は以下のようなメソッドを持っています:
      • play(): 再生を開始
      • pause(): 再生を一時停止
      • load(): ソースを再読み込み
      • その他、再生位置や音量を制御するプロパティ

注意点と補足

  1. 自動再生の制限

    • ブラウザによっては、ユーザー操作なしに音声や動画の自動再生がブロックされる場合があります。
    • play() メソッドを呼び出した際に、Promise が拒否され(エラーが発生し)、再生が開始されないことがあります。
    audioRef.current.play().catch((error) => {
      // 再生に失敗した場合の処理
      console.error('再生エラー:', error);
      // 必要に応じてユーザーに通知や、代替処理を実行
    });
    
  2. async/await の使用

    • play() メソッドは Promise を返すため、await を使用して再生が開始されるのを待つことができます。
    const handleSongSelected = async (song) => {
      try {
        setSelectedSong(song);
        audioRef.current.src = song.preview_url;
        await audioRef.current.play(); // 再生が開始されるのを待つ
        setIsPlay(true);
      } catch (error) {
        // 再生エラーの処理
        console.error('再生エラー:', error);
      }
    };
    
  3. ユーザー体験の向上

    • 再生ボタンを設けて、ユーザーの操作によって再生を開始することで、自動再生の制限を回避できます。
    <button onClick={() => audioRef.current.play()}>再生</button>
    

まとめ

  • play() メソッドは、ブラウザの標準的なWeb APIである HTMLMediaElement インターフェースから提供されるものです。
  • audioRef.current が <audio> 要素を参照しているため、その play() メソッドを呼び出すことで音声の再生を制御できます。
  • メディアの再生制御には他にも pause()currentTimevolume など、多くのプロパティやメソッドがあります。
このスクラップは23日前にクローズされました