as

Settings
Sign out
Notifications
Alexa
亚马逊应用商店
AWS
文档
Support
Contact Us
My Cases
新手入门
设计和开发
应用发布
参考
支持

获取视频列表

获取视频列表

现在有了可以将数据传递给VideoCard组件的登陆屏幕,下一步是调用API来检索登陆屏幕可以使用的视频数据。

我们将使用React hook来管理应用的数据和状态 (useState),并指示应用在加载时检索数据 (useEffect)。hook本质上是可重复使用的逻辑,可以用在您的代码中。我们不会在本指南中详细解释挂钩,但建议您在以下网址阅读相关更多信息:https://reactjs.org/docs/hooks-intro.html(仅提供英文版)。

定义数据

在检索视频数据之前,我们需要定义数据在TypeScript中的格式。

  • 打开LandingScreen.tsx
  • 导入语句后,使用以下定义创建一个名为IVideo的接口。
interface IVideo {
  id: string;
  title: string;
  description: string;
  duration: number;
  thumbURL: string;
  imgURL: string;
  videoURL: string;
  categories: string[];
  channel_id: number;
}

存储数据

现在我们需要存储视频数据的地方以及更新这类数据的方法,这将使用户界面重新呈现使用这些数据的任何组件(对事件“做出反应”)。useState hook可以满足我们的这两个需求。它返回一个变量和一个更新变量的函数。每当使用该函数更新变量时,都会导致使用该变量的用户界面组件重新呈现(对事件“做出反应”)。

  • LandingScreen.tsx中更新“react”的导入语句以纳入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调用。该函数将使用Fetch API来调用我们的API终端节点(未来将支持axios,它是用于调用其他API的另一个API)。我们将使用setVideo函数来设置从fetch中返回的视频数组。因为fetch返回一个Promise对象,所以我们需要使用.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语句,将视频的值设置为响应对象的相应部分。
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);
    });
};

(这是常见的JavaScript和ES6语法,使用Promise从API中检索数据。需要注意的主要一点是,我们从API响应中获取数据并调用setVideos。)

测试数据检索

现在我们有了可以返回和更新视频列表的函数,让我们来测试一下。

  • getAllVideos()定义后的任意位置添加调用。
  • 添加console.log语句以在调用getAllVideos()之前和之后记录videos
console.log(videos);
getAllVideos();
console.log(videos);
  • 重新加载应用。您会注意到,虽然模拟器显示正确,但Metro终端显示的是无休止的日志语句循环。根据您运行应用的方式,可以在不同的位置找到日志信息:

  • 如果使用VSCode,请查看React Native输出面板

  • 如果使用CLI,请在Metro终端查看

之所以发生这种情况,是因为每次videos变量变化时,都会重新呈现LandingScreen。每次重新呈现时,我们都会再次检索视频,这会导致这种无休止的循环。

console.log(videos); // 视频未定义。
getAllVideos(); // 这会导致无休止的重新呈现。
console.log(videos); // 这会向我们展示我们正在无休止重新呈现

添加useEffect hook

为了防止这种情况发生,我们需要在useEffect挂钩中调用getAllVideos。这个hook让我们能够仅在视频在两次呈现之间发生变化时才重新呈现。

  • 更新“react”的导入语句以包含useEffect。这是一个命名导出,因此在大括号中将置于useState旁边。
import React, {useState, useEffect} from 'react';
  • 使用useEffect hook结束对getAllVideos()的调用。这个hook带有一个箭头函数,您将在其中调用getAllVideos()
console.log(videos);

useEffect(() => {
  getAllVideos();
}, []);

console.log(videos);

注意第二个参数是[]。这个参数规定useEffect只呈现一次。由于我们只需要检索视频一次,因此这个参数可以满足我们的目的。有关使用useEffect hook的更多高级方法,请参阅React Native文档

  • 重新加载应用,您会看到Metro终端窗口不再有无休止的循环。
  • 鉴于我们已经解决了无休止循环的问题,可以删除console.log()语句。

创建视频类别

最后,与其存储所有视频,不如将视频筛选成特定的类别,以便在用户界面中按类别分组显示它们。

  • 修改状态变量以改为保存我们的分类视频:
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('Costa Rica Islands'),
      );
      
      const underwater = data.testData.filter(
        (video: IVideo) =>
          video.categories &&
          video.categories.includes('Costa Rica Underwater'),
      );
      
      setIslandVideos(islands);
      setUnderwaterVideos(underwater);
    })
    .catch((error) => {
      console.log(error);
    });
};

现在我们有两个独立的视频数组,islandVideos和underwaterVideos,我们可以用它们在用户界面中显示不同类别的视频。

更新后的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('Costa Rica Islands'),
        );
        const underwater = data.testData.filter(
          (video: IVideo) =>
            video.categories &&
            video.categories.includes('Costa Rica Underwater'),
        );
        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;