as

Settings
Sign out
Notifications
Alexa
Amazonアプリストア
AWS
ドキュメント
Support
Contact Us
My Cases
開発
設計と開発
公開
リファレンス
サポート

ビデオリストの取得

ビデオリストの取得

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から返されるビデオの配列を設定します。fetchPromiseオブジェクトを返すため、.thenを使用して非同期メソッドでコードを処理する必要があります。

クリップボードにコピーしました。

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;