ビデオリストの取得
VideoCardコンポーネントにデータを渡すランディング画面を作成したので、次の手順では、APIを呼び出して、ランディング画面で使用できるビデオデータを取得します。
アプリのデータと状態を管理し(useState)、読み込み時にデータを取得するようアプリに指示する(useEffect)ためにReactフックを使用します。フックは基本的にコードで使用できる再利用可能なロジックです。このガイドではフックの詳しい説明はしません。詳細については、こちら(https://ja.legacy.reactjs.org/docs/hooks-intro.html)を参照してください。
データの定義
ビデオデータを取得する前に、データがTypeScriptでどのように表示されるかを定義する必要があります。
LandingScreen.tsxを開きます。- importステートメントの後に、
IVideoというインターフェイスを作成して次のように定義します。
interface IVideo {
id: string;
title: string;
description: string;
duration: number;
thumbURL: string;
imgURL: string;
videoURL: string;
categories: string[];
channel_id: number;
}
データの保存
ここで、ビデオデータを保存する場所と、それを使用するコンポーネントをUIで再レンダリングする(イベントに「反応する」)ための更新方法が必要です。useStateフックはこの両方に対応し、変数と、変数を更新する関数を返します。関数を使用して変数が更新されると、その変数を使用するUIコンポーネントが(イベントに「応答」して)再レンダリングされます。
LandingScreen.tsxで、「react」のimportステートメントを更新してuseStateを含めます。useStateは名前付きエクスポートであるため、中かっこで囲む必要があります。エラーを回避するために、ステートメントの構文を変更することも必要です。
import React, {useState} from 'react';
- LandingScreenアロー関数の最上部に、次のステートメントを追加します。
const [videos, setVideos] = useState<IVideo[]>();
更新されたLandingScreen.tsxのコードは次のようになります。
import React, {useState} from 'react';
import Header from '../components/Header';
import VideoCard from '../components/VideoCard';
interface IVideo {
id: string;
title: string;
description: string;
duration: number;
thumbURL: string;
imgURL: string;
videoURL: string;
categories: string[];
channel_id: number;
}
const LandingScreen = () => {
const [videos, setVideos] = useState<IVideo[]>();
return (
<>
<Header />
<VideoCard
title="マイビデオ"
description="マイビデオの説明"
imgURL="https://le1.cdn01.net/videos/0000169/0169322/thumbs/0169322__002f.jpg"
/>
</>
);
};
export default LandingScreen;
データの取得
次に、API呼び出しを実行する関数を作成します。この関数では、APIエンドポイントの呼び出しにFetch APIを使用します(ほかのAPIの呼び出しに使用されるAxios APIは今後サポートされる予定です)。また、setVideo関数を使用して、fetchから返されるビデオの配列を設定します。fetchはPromiseオブジェクトを返すため、.thenを使用して非同期メソッドでコードを処理する必要があります。
LandingScreen.tsxにurlというコンストラクターを作成し、https://d2ob7xfxpe6plv.cloudfront.net/TestData.jsonに設定します。
const url = "https://d2ob7xfxpe6plv.cloudfront.net/TestData.json";
- LandingScreenの本文内(つまり、LandingScreenアロー関数内)に
getAllVideos()というアロー関数を作成します。 fetchを呼び出してURLを渡します。.thenを追加してレスポンスを待機し、jsonに変換します。
const getAllVideos = () => {
fetch(url).then(response => response.json());
};
- 別の
.thenステートメントを追加して、videosの値をレスポンスオブジェクトの適切な部分に設定します。
const getAllVideos = () => {
fetch(url)
.then(response => response.json())
.then(data => setVideos(data.testData));
};
- エラーメッセージに
.catchを追加し、console.logを使用して出力します。
最終的なgetAllVideos()関数は次のようになります。
const getAllVideos = () => {
fetch(url)
.then(response => response.json())
.then(data => setVideos(data.testData))
.catch(error => {
console.log(error);
});
};
(これは、Promiseを使用してAPIからデータを取得する一般的なJavaScriptとES6の構文です。重要なのは、APIレスポンスからデータを取得してsetVideosを呼び出すという点です。)
テストデータの取得
ビデオのリストを返したり更新したりできる関数ができたので、テストしてみましょう。
- 定義後に
getAllVideos()の呼び出しを追加します。 getAllVideos()の呼び出しの前後にvideosを出力するには、console.logステートメントを追加します。
console.log(videos);
getAllVideos();
console.log(videos);
-
アプリを再読み込みします。シミュレーターは正しく表示されているのに、Metroターミナルにはログステートメントの無限ループが表示されます。ログ情報は、アプリの実行方法に応じてさまざまな場所にあります。
-
VSCodeを使用している場合は、React Nativeの出力パネルを確認してください。

- CLIを使用している場合は、Metroターミナルを確認してください。
これは、videos変数が変更されるたびにLandingScreenが再レンダリングされるためです。再レンダリングごとにビデオが再度取得されるため、無限ループが生じます。
console.log(videos); // videosは未定義です。
getAllVideos();// これにより、無限に再レンダリングされます。
console.log(videos); // これで無限に再レンダリングされることがわかります。
useEffectフックの追加
これを回避するには、getAllVideosへの呼び出しをuseEffectフックに配置する必要があります。このフックを使用すると、レンダリングの合間にvideosが変更された場合にのみ再レンダリングすることができます。
- 「react」のimportステートメントを更新して
useEffectを含めます。このエクスポートは名前付きエクスポートであるため、中かっこ内でuseStateと並べます。
import React, {useState, useEffect} from 'react';
getAllVideos()の呼び出しをuseEffectフックでラップします。このフックはアロー関数を取り込み、getAllVideos()を呼び出します。
console.log(videos);
useEffect(() => {
getAllVideos();
}, []);
console.log(videos);
2番目のパラメーターは[]です。このパラメーターは、1回だけレンダリングするようにuseEffectに指示します。videosを取得する必要があるのは1回だけのため、目的に合っています。useEffectフックの高度な使用方法については、React Nativeのドキュメントを参照してください。
- アプリを再読み込みすると、Metroのターミナルウィンドウに無限ループが発生しなくなります。
- 無限ループを解決したので、console.log()ステートメントを削除します。
ビデオカテゴリの作成
最後に、すべての動画を保存する代わりに、ビデオを特定のカテゴリにフィルタリングして、UIでカテゴリ別にグループ化して表示しましょう。
- カテゴリ分けされた動画が代わりに保存されるようにstate変数を変更してください。
const [islandVideos, setIslandVideos] = useState<IVideo[]>([]);
const [underwaterVideos, setUnderwaterVideos] = useState<IVideo[]>([]);
- JavaScriptのfilter()メソッドを使用してAPIから動画を受け取った後、getAllVideos関数を変更して動画をカテゴリ別にフィルタリングします。
const getAllVideos = () => {
fetch(url)
.then((response) => response.json())
.then((data) => {
const islands = data.testData.filter(
(video: IVideo) =>
video.categories && video.categories.includes('コスタリカの島々'),
);
const underwater = data.testData.filter(
(video: IVideo) =>
video.categories &&
video.categories.includes('コスタリカの水中'),
);
setIslandVideos(islands);
setUnderwaterVideos(underwater);
})
.catch((error) => {
console.log(error);
});
};
これで、islandVideosとunderwaterVideosという2つの別々のビデオ配列ができました。これらを使用してUIにさまざまなカテゴリのビデオを表示できます。
更新されたLandingScreen.tsxは次のようになります。
import React, {useState, useEffect} from 'react';
import Header from '../components/Header';
import VideoCard from '../components/VideoCard';
interface IVideo {
id: string;
title: string;
description: string;
duration: number;
thumbURL: string;
imgURL: string;
videoURL: string;
categories: Array<string>;
channel_id: number;
}
const LandingScreen = () => {
const [islandVideos, setIslandVideos] = useState<IVideo[]>([]);
const [underwaterVideos, setUnderwaterVideos] = useState<IVideo[]>([]);
const url = 'https://d2ob7xfxpe6plv.cloudfront.net/TestData.json';
const getAllVideos = () => {
fetch(url)
.then((response) => response.json())
.then((data) => {
const islands = data.testData.filter(
(video: IVideo) =>
video.categories && video.categories.includes('コスタリカの島々'),
);
const underwater = data.testData.filter(
(video: IVideo) =>
video.categories &&
video.categories.includes('コスタリカの水中'),
);
setIslandVideos(islands);
setUnderwaterVideos(underwater);
})
.catch((error) => {
console.log(error);
});
};
useEffect(() => {
getAllVideos();
}, []);
return (
<>
<Header />
<VideoCard
title="マイビデオ"
description="マイビデオの説明"
imgURL="https://le1.cdn01.net/videos/0000169/0169322/thumbs/0169322__002f.jpg"
/>
</>
);
};
export default LandingScreen;

