2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

React学習のために音楽検索サービスを開発してみた

Last updated at Posted at 2025-05-09

1.はじめに

この記事は,React初心者が学習のために音楽検索サービスを開発したサイトです.
ぜひ,皆様も使ってください.
コード不備とか,もっといい実装があったら教えてくれると嬉しいです!!!

2.完成系イメージ

  • フォームに,調べたい音楽を検索する
  • 一覧として,検索結果が表示される

3.学習トピック

  • Reactを使用したAPIの取得をイメージ
  • コンポーネント分割の記述とイメージ
  • useStateなどの状態管理

4.実装

まずは,部品に分けて考える.

  • 検索フォーム
  • 検索フォームのvalueを受け取ったバックでのAPI処理
  • 処理結果を曲一覧として反映する

では,実際に ./src/Componentsフォルダの中に,フォルダに以下のようなコンポーネントフォルダを作成しましょう!

./src/Components/MusicForm.tsx // 入力フォーム
./src/Components/MusicSearch.tsx // API処理
./src/Components/MusicList.tsx // 検索結果表示

MusicForm.tsx


MusicForm.tsx
import React from 'react'
import { useState } from 'react'

type MusicFormProps = 
{
    onSearch: (query: string) => void;
}
const MusicForm: React.FC<MusicFormProps> = ({ onSearch }) => 
{
    const [query, setQuery] = useState<string>('');
    return (
        <div>
            <form onSubmit={handleSubmit}>
                <input 
                    type='text'
                    value='query'
                    onChange='handleChange'
                    placeHolder='曲名・アーティスト名を検索'
                />
                <button type='submit' value='検索' />
            </form>
        </div>
    );
}

export default MusicForm

まずは,フォームを作るものなのでこのように書きましょう.
onSearchは,親コンポーネント(今回はMusicSerach)から,処理のpropsを受け取るために宣言します.
API処理をするため,検索結果をqueryとして渡すことはわかっていますので,あらかじめqueryを設定する.
submitしたとき,フォームに入力したときの挙動を指定したいため,handleSubmit, handleChangeを作る.
では,いかにhandleSubmit, handleChangeを書いていく.

MusicForm.tsx
const handleSubmit = (e: FormEvent<HTMLInputElement>) =>
{
    e.preventDefault(); // リロードしないための処理
    if (query.trim()) // 空白を排除
    {
        onSearch(query);
    }
}
const handleChange = (e: ChangeEvent<HTMLInputElement>) =>
{
    setQuery(e.target.value);
}

handleSubmitでは,クリックした時の処理を記述しています.
クリックした時,ページをリロードせず,queryを整理して親コンポーネントから受け取る処理にqueryを渡します.
handleChangeではインプットフォームの内容が変更された時に実行されます.
インプットフォームの内容をqueryとしてセットするため,setQueryにe.target.valueを引数として渡します.

MusicSearch.tsx


では,API処理していきましょう.
今回使うAPIは,iTunes Search APIです.

この方の記事がわかりやすかったので,参考にしてAPIで結果を返してもらいます.

Song.ts
type Song = 
{
    trackId: number;
    trackName: string;
    artistName: string;
    artworkUrl100: string;
    previewUrl: string;
};

MusicSearch.tsx
const MusicSearch: React.FC = () =>
{
    const [songs, setSongs] = useState<Song[]>([]);
    const [loading, setLoading] = useState(false);

    const handleSearch = async (query: string) => {
        setLoading(true);
    try {
        const response = await fetch(
        `https://itunes.apple.com/search?term=${encodeURIComponent(query)}&entity=song&limit=10`);
        const data = await response.json();
        setSongs(data.results);
    } catch (error) {
        console.log('検索に失敗しました', error);
    } finally {
        setLoading(false);
    }
  };
}

コードを解説していきます.
まず,先ほどの記事から,曲を表示する時に必要なレスポンスキーのpropsの型を宣言します.それが,Song.tsです.
そして,songs, setSongsを宣言していきます.Songの一覧を表示したいため,Songの配列で宣言します.初期値は空配列で宣言しましょう.
基本的には,JavaScriptsでの処理と変わりません.asyncを使用して,Promiseを受け取るようにします.awaitを使用して,非同期処理を実現します.
API取得でエラーが発生する場合が多いので,try-catch文を使用して,エラー処理を行います.
encodeURLComponents()を使用することで,安全にURLをエンコードすることができます.encodeURLencodeURLComponentsの違いについては,この方がわかりやすく解説してくれていますので,ぜひご参照ください.

以上の部分がAPIの処理の部分になります.
これから,その処理したものをpropsの受け渡しで,反映していきます.

MusicSearch.tsx
// 上記の続きから
return (
    <div>
        <Form onSearch={ onSearch }></Form>
        {loading && <p>検索中...</p>}
        {?loading && songs.length === 0 && <p>検索結果が見つかりませんでした</p>}
        <ul>
            {songs.map((song) => (<MusicList key={trackId} song={song} />))}
        </ul>
    </div>
);

このように,FormonSearchPropsへ,handleSearchを渡します.そうすることで,queryによってAPI処理をしてくれるようになります.
途中のloadingの処理は,同期処理が実行されている最中は検索中同期処理が終わったけど取得できない時は検索結果が見つかりませんでしたというふうに表示する処理です.
songsで,APIで帰ってきた情報が配列で格納されています.
それを一つずつ表示していきたい.
そのための,MusicListコンポーネントです.
では,mapを使用して配列の要素を一つずつ処理していきましょう.

MusicList


MusicList.tsx
import React from 'react';
import { Song } from '../Song';

type ListProps = {
  song: Song;
};

const MusicList: React.FC<ListProps> = ({ song }) => {
  return (
    <li className="music-list-item">
      <img src={song.artworkUrl100} alt={song.trackName} />
      <p>{song.trackName} - {song.artistName}</p>
      <audio controls src={song.previewUrl} />
    </li>
  );
};

export default MusicList;

5.終わりに

今回,自分自身も初心者で,色々試行錯誤しながら今回の音楽検索サービスを開発し,記事化しました.
しかし,作り終わって今記事を書いている最中にuseEffectなどのHooksがあるんだ,という技術的な発見, もっとこうした方が良かったなぁなどのアイディアをたくさん見つけることができました.
これからも頑張って記事を書いていきます.


全体ソースコードはこちら

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?