使用 Fetch API 和 axios 在 React 中获取数据

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

如果您是 React 新手,并且可能只尝试过构建待办事项和计数器应用程序,那么您可能还没有遇到过需要为您的应用程序提取数据的需求。您很可能会遇到需要执行此操作的时候,因为 React 应用程序最 适合 处理数据和状态的情况。

您可能需要处理的第一组数据可能会硬编码到您的 React 应用程序中,就像我们在来自我们 错误边界教程 的此演示中所做的那样。

查看 Kingsley Silas Chijioke (@kinsomicrote) 在 CodePen 上的 Pen error boundary 0

如果您想处理来自 API 的数据怎么办?这就是本教程的目的。具体来说,我们将使用 Fetch APIaxios 作为请求和使用数据的示例。

Fetch API

Fetch API 提供了一个获取资源的接口。我们将使用它从第三方 API 获取数据,并了解如何在从内部构建的 API 获取数据时使用它。

使用 Fetch 与第三方 API

查看 Kingsley Silas Chijioke (@kinsomicrote) 在 CodePen 上的 Pen React Fetch API Pen 1

我们将从 JSONPlaceholder(一个用于测试的虚假在线 REST API)获取随机用户。让我们首先创建我们的组件并声明一些默认状态。

class App extends React.Component {
  state = {
    isLoading: true,
    users: [],
    error: null
  }

  render() {
    <React.Fragment>
    </React.Fragment>
  }
}

网络请求数据时,必然会存在延迟。可能是几秒钟,也可能是几毫秒。无论哪种方式,在此延迟期间,最好让用户知道在请求处理期间正在发生某些事情。

为此,我们将使用 isLoading 来显示加载消息请求的数据。当 isLoadingfalse 时,将显示数据,否则将在屏幕上显示加载消息。因此,render() 方法将如下所示

render() {
  const { isLoading, users, error } = this.state;
  return (
    <React.Fragment>
      <h1>Random User</h1>
      // Display a message if we encounter an error
      {error ? <p>{error.message}</p> : null}
      // Here's our data check
      {!isLoading ? (
        users.map(user => {
          const { username, name, email } = user;
          return (
            <div key={username}>
              <p>Name: {name}</p>
              <p>Email Address: {email}</p>
              <hr />
            </div>
          );
        })
      // If there is a delay in data, let's let the user know it's loading
      ) : (
        <h3>Loading...</h3>
      )}
    </React.Fragment>
  );
}

代码基本上是这样做的

  1. 从应用程序状态中解构 isLoadinguserserror,这样我们就不必一直键入 this.state 了。
  2. 如果应用程序遇到建立连接的错误,则打印一条消息
  3. 检查数据是否正在加载
  4. 如果未加载,则我们必须拥有数据,因此我们显示它
  5. 如果正在加载,则我们仍然需要处理它,并在应用程序工作时显示“加载中…”

为了使步骤 3-5 起作用,我们需要发出请求以从 API 获取数据。对于我们的示例,JSONplaceholder API 将派上用场。

fetchUsers() {
  // Where we're fetching data from
  fetch(`https://jsonplaceholder.typicode.com/users`)
    // We get the API response and receive data in JSON format...
    .then(response => response.json())
    // ...then we update the users state
    .then(data =>
      this.setState({
        users: data,
        isLoading: false,
      })
    )
    // Catch any errors we hit and update the app
    .catch(error => this.setState({ error, isLoading: false }));
}

我们创建一个名为 fetchUser() 的方法,并使用它来执行您可能想到的操作:从 API 端点请求用户数据并为我们的应用程序获取它。Fetch 是一个基于 Promise 的 API,它返回一个响应对象。因此,我们使用 json() 方法获取存储在数据中的响应对象,并将其用于更新应用程序中用户的 state。我们还需要将 isLoading 的 state 更改为 false,以便我们的应用程序知道加载已完成,并且可以渲染数据。

Fetch 基于 Promise 的事实意味着我们还可以使用 .catch() 方法捕获错误。遇到的任何错误都用作更新错误 state 的值。方便!

应用程序第一次渲染时,数据尚未接收——可能需要几秒钟。当应用程序 state 可以访问以进行更新和重新渲染时,我们希望触发方法以获取用户。React 的 componentDidMount() 是最适合此操作的地方,因此我们将 fetchUsers() 方法放在其中。

componentDidMount() {
  this.fetchUsers();
}

使用 Fetch 与自有 API

到目前为止,我们已经了解了如何在应用程序中使用其他人的数据。但是,如果我们在使用自己的 API 中自己的数据怎么办?这就是我们现在要介绍的内容。

查看 Kingsley Silas Chijioke (@kinsomicrote) 在 CodePen 上的 Pen React Fetch API Pen 2

我构建了一个 API,可在 GitHub 上使用。您获得的 JSON 响应已放置在 AWS 上——这就是我们将用于本教程的内容。

与之前一样,让我们创建我们的组件并设置一些默认状态。

class App extends React.Component {
  state = {
    isLoading: true,
    posts: [],
    error: null
  }

  render() {
    <React.Fragment>
    </React.Fragment>
  }
}

我们循环遍历数据的方法将与之前使用的方法不同,但这仅仅是因为数据结构不同,这将有所不同。您可以在这里查看我们的数据结构 here 和我们从 JSONPlaceholder 获得的数据结构之间的区别。

以下是我们的 API 的 render() 方法的外观

render() {
  const { isLoading, posts, error } = this.state;
  return (
    <React.Fragment>
      <h1>React Fetch - Blog</h1>
      <hr />
      {!isLoading ? Object.keys(posts).map(key => <Post key={key} body={posts[key]} />) : <h3>Loading...</h3>}
    </React.Fragment>
  );
}

让我们分解一下逻辑

{
  !isLoading ? 
  Object.keys(posts).map(key => <Post key={key} body={posts[key]} />) 
  : <h3>Loading...</h3>
}

isLoading 不为 true 时,我们返回一个数组,遍历它并将信息作为 props 传递给 Post 组件。否则,在应用程序工作时显示“加载中…”消息。与之前非常相似。

获取帖子的方法将类似于第一部分中使用的方法。

fetchPosts() {
  // The API where we're fetching data from
  fetch(`https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json`)
    // We get a response and receive the data in JSON format...
    .then(response => response.json())
    // ...then we update the state of our application
    .then(
      data =>
        this.setState({
          posts: data,
          isLoading: false,
        })
    )
    // If we catch errors instead of a response, let's update the app
    .catch(error => this.setState({ error, isLoading: false }));
}

现在,我们可以在 componentDidMount() 方法中调用 fetchPosts 方法

componentDidMount() {
  this.fetchPosts();
}

在 Post 组件中,我们遍历接收到的 props 并渲染每个帖子的标题和内容

const Post = ({ body }) => {
  return (
    <div>
      {body.map(post => {
        const { _id, title, content } = post;
        return (
          <div key={_id}>
            <h2>{title}</h2>
            <p>{content}</p>
            <hr />
          </div>
        );
      })}
    </div>
  );
};

就是这样!现在我们知道了如何使用 Fetch API 从不同的来源请求数据并在应用程序中使用它。击个掌。✋

axios

好的,所以我们花了大量时间研究 Fetch API,现在我们将把注意力转向 axios。

与 Fetch API 一样,axios 也是我们可以在应用程序中发出数据请求的一种方式。axios 的亮点在于它允许您向 REST 端点发送异步请求。在 React 项目中使用 REST API 时,这非常有用,例如 无头 WordPress CMS

关于 Fetch 是否比 axios 更好,反之亦然,一直存在争议。我们在这里不会深入探讨,因为,您可以为正确的工作选择正确的工具。如果您对各方观点感兴趣,可以阅读 herehere

使用 axios 与第三方 API

查看 Kingsley Silas Chijioke (@kinsomicrote) 在 CodePen 上的 Pen React Axios 1 Pen

与我们对 Fetch API 所做的一样,让我们首先从 API 请求数据。对于此示例,我们将从 Random User API 获取随机用户。

首先,我们像之前每次一样创建 App 组件

class App extends React.Component {
  state = {
    users: [],
    isLoading: true,
    errors: null
  };

  render() {
    return (
      <React.Fragment>
      </React.Fragment>
    );
  }
}

想法仍然相同:检查加载是否正在进行,并渲染我们收到的数据或让用户知道事情仍在加载。

要向 API 发出请求,我们需要创建一个函数。我们将函数称为 getUsers()。在其中,我们将使用 axios 向 API 发出请求。让我们在进一步解释之前看看它是什么样子。

getUsers() {
  // We're using axios instead of Fetch
  axios
    // The API we're requesting data from
    .get("https://randomuser.me/api/?results=5")
    // Once we get a response, we'll map the API endpoints to our props
    .then(response =>
      response.data.results.map(user => ({
        name: `${user.name.first} ${user.name.last}`,
        username: `${user.login.username}`,
        email: `${user.email}`,
        image: `${user.picture.thumbnail}`
      }))
    )
    // Let's make sure to change the loading state to display the data
    .then(users => {
      this.setState({
        users,
        isLoading: false
      });
    })
    // We can still use the `.catch()` method since axios is promise-based
    .catch(error => this.setState({ error, isLoading: false }));
}

与 Fetch 示例非常不同,对吧?基本结构实际上非常相似,但现在我们正在处理端点之间的数据映射。

GET 请求作为参数从 API URL 传递。我们从 API 获取的响应包含一个名为 data 的对象,该对象包含其他对象。我们想要的信息在 data.results 中可用,它是一个包含单个用户数据的对象的数组。

我们再次在 componentDidMount() 方法中调用我们的方法

componentDidMount() {
  this.getUsers();
}

或者,您可以执行此操作,并将这两步基本合并

componentDidMount() {
  axios
    .get("https://randomuser.me/api/?results=5")
    .then(response =>
      response.data.results.map(user => ({
        name: `${user.name.first} ${user.name.last}`,
        username: `${user.login.username}`,
        email: `${user.email}`,
        image: `${user.picture.thumbnail}`
      }))
    )
    .then(users => {
      this.setState({
        users,
        isLoading: false
      });
    })
    .catch(error => this.setState({ error, isLoading: false }));
}

如果您是从您的机器本地编码,则可以暂时编辑 getUsers() 函数使其如下所示

getUsers() {
  axios
    .get("https://randomuser.me/api/?results=5")
    .then(response => console.log(response))
    .catch(error => this.setState({ error, isLoading: false }));
}

您的控制台应该会得到类似于此的内容

我们遍历 results 数组以获取每个用户所需的信息。然后,用户数组用于为我们的 users state 设置新值。完成此操作后,我们就可以更改 isLoading 的值了。

默认情况下,isLoading 设置为 true。当 users 的状态更新时,我们希望将 isLoading 的值更改为 false,因为这是我们的应用程序用来从“加载中…”切换到渲染数据的提示。

render() {
  const { isLoading, users } = this.state;
  return (
    <React.Fragment>
      <h2>Random User</h2>
      <div>
        {!isLoading ? (
          users.map(user => {
            const { username, name, email, image } = user;
            return (
              <div key={username}>
                <p>{name}</p>
                <div>
                  <img src={image} alt={name} />
                </div>
                <p>{email}</p>
                <hr />
              </div>
            );
          })
        ) : (
          <p>Loading...</p>
        )}
      </div>
    </React.Fragment>
  );
}

如果将 users 状态记录到控制台,您会发现它是一个对象数组。

空数组显示在获取数据之前的值。返回的数据仅包含各个用户的 nameusernameemail addressimage,因为这些是我们映射出的端点。当然,API 中还有更多可用数据,但我们必须将它们添加到我们的 getUsers 方法中。

使用 Axios 与您自己的 API

查看 Kingsley Silas Chijioke(@kinsomicrote)在 CodePen 上的笔 React Axios 2 Pen

您已经了解了如何将 Axios 与第三方 API 结合使用,但我们可以看看从我们自己的 API 请求数据的情况,就像我们使用 Fetch API 所做的那样。事实上,让我们使用与 Fetch 使用的相同的 JSON 文件,以便我们可以看到这两种方法之间的区别。

以下是所有内容的组合。

class App extends React.Component {
  // State will apply to the posts object which is set to loading by default
  state = {
    posts: [],
    isLoading: true,
    errors: null
  };
  // Now we're going to make a request for data using axios
  getPosts() {
    axios
      // This is where the data is hosted
      .get("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json")
      // Once we get a response and store data, let's change the loading state
      .then(response => {
        this.setState({
          posts: response.data.posts,
          isLoading: false
        });
      })
      // If we catch any errors connecting, let's update accordingly
      .catch(error => this.setState({ error, isLoading: false }));
  }
  // Let's our app know we're ready to render the data
  componentDidMount() {
    this.getPosts();
  }
  // Putting that data to use
  render() {
    const { isLoading, posts } = this.state;
    return (
      <React.Fragment>
        <h2>Random Post</h2>
        <div>
          {!isLoading ? (
            posts.map(post => {
              const { _id, title, content } = post;
              return (
                <div key={_id}>
                  <h2>{title}</h2>
                  <p>{content}</p>
                  <hr />
                </div>
              );
            })
          ) : (
            <p>Loading...</p>
          )}
        </div>
      </React.Fragment>
    );
  }
}

此方法与使用 Axios 从第三方获取数据之间的主要区别在于数据的格式。我们以这种方式直接获取 JSON,而不是映射端点。

我们从 API 获取的帖子数据用于更新组件的 posts 状态的值。有了这个,我们可以在 render() 中遍历帖子数组。然后,我们使用 ES6 解构获取每个帖子的 idtitlecontent,然后将其渲染给用户。

就像我们之前做的那样,显示的内容取决于 isLoading 的值。当我们使用从 API 获取的数据为 posts 设置新状态时,我们也必须为 isLoading 设置新状态。然后我们终于可以告知用户数据正在加载或渲染我们收到的数据。

async 和 await

Axios 基于 Promise 的特性允许我们做的另一件事是利用 asyncawait。使用此功能,getPosts() 函数将如下所示。

async getPosts() {
  const response = await axios.get("https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json");
  try {
    this.setState({
      posts: response.data.posts,
      isLoading: false
    });
  } catch (error) {
    this.setState({ error, isLoading: false });
  }
}

基本实例

使用 Axios,可以创建一个基本实例,在其中输入我们 API 的 URL,如下所示。

const api = axios.create({
  baseURL: "https://s3-us-west-2.amazonaws.com/s.cdpn.io/3/posts.json"
});

…然后像这样使用它。

async getPosts() {
  const response = await api.get();
  try {
    this.setState({
      posts: response.data.posts,
      isLoading: false
    });
  } catch (error) {
    this.setState({ error, isLoading: false });
  }
}

这只是抽象 API URL 的一种好方法。

现在,获取所有数据!

在构建 React 应用程序时,您会遇到许多需要处理 API 数据的场景。希望您现在已经做好准备,可以使用各种来源的数据,并可以选择如何请求它。

想要处理更多数据?Sarah 最近写了一篇关于从公共 API 列表中 创建您自己的无服务器 API 的步骤的文章。