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
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を書いていく.
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で結果を返してもらいます.
type Song =
{
trackId: number;
trackName: string;
artistName: string;
artworkUrl100: string;
previewUrl: string;
};
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をエンコードすることができます.encodeURL
とencodeURLComponents
の違いについては,この方がわかりやすく解説してくれていますので,ぜひご参照ください.
以上の部分がAPIの処理の部分になります.
これから,その処理したものをpropsの受け渡しで,反映していきます.
// 上記の続きから
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>
);
このように,Form
にonSearchProp
sへ,handleSearch
を渡します.そうすることで,query
によってAPI処理をしてくれるようになります.
途中のloading
の処理は,同期処理が実行されている最中は検索中
同期処理が終わったけど取得できない時は検索結果が見つかりませんでした
というふうに表示する処理です.
songsで,APIで帰ってきた情報が配列で格納されています.
それを一つずつ表示していきたい.
そのための,MusicListコンポーネントです.
では,mapを使用して配列の要素を一つずつ処理していきましょう.
MusicList
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があるんだ,という技術的な発見, もっとこうした方が良かったなぁなどのアイディアをたくさん見つけることができました.
これからも頑張って記事を書いていきます.
全体ソースコードはこちら