React API 调用

摘要:在本教程中,您将学习使用 Web 浏览器提供的内置 Fetch API 在 React 应用程序中调用 API。

维基百科搜索 React 应用程序简介

我们将创建一个新的 允许您搜索维基百科文章的 React 应用程序。为此,React 应用程序需要调用维基百科搜索 API

react api call - wikipedia search app

React 应用程序将如下所示

react api call - wikipedia search app prototype

我们可以将此 React 应用程序分解为以下组件

react api call - wikipedia search

以下是组件层次结构

在此组件层次结构中

  • App 组件是一个父组件,包含其他组件。
  • SearchBar 组件允许您键入搜索词并按 Enter 键提交。
  • ArticleList 组件负责呈现搜索结果,其中包括文章列表。
  • Article 组件呈现搜索结果中的每篇文章。

道具和状态设计

在创建应用程序之前,我们需要回答一些问题

  • 应用程序的状态是什么?
  • 哪个组件应该处理 API 调用?
  • 我们应该如何设计 道具系统

该应用程序显示文章列表,因此它应该有一个作为状态变量的文章数组

const [articles, setArticles] = useState([]);Code language: JavaScript (javascript)

由于 App 组件使用 ArticleList 组件来呈现文章列表,因此它应该将 articles 数组作为道具传递给 ArticleList

此外,ArticleList 应该将每个 article 作为道具传递给 Article 组件以呈现每篇文章。

如果我们在 SearchBar 组件中调用维基百科 API,我们可以获得文章列表。但是,我们如何将文章列表从 SearchBar 组件传递到 App 组件?

通常,React 允许我们将道具从父组件传递给子组件,而不是从子组件传递给父组件或在兄弟组件之间传递。

为了克服这个限制,我们可以将回调函数从 App 组件作为道具传递给 SearchBar 组件。

当用户提交搜索表单时,我们可以调用回调函数 onSearch 来更新 App 组件的 articles 状态

react api call - states & props design

创建一个新的 React 应用程序

首先,在您的计算机上打开一个终端,并使用 create-react-app 命令创建一个新的 React 应用程序

npx create-react-app wiki-searchCode language: JavaScript (javascript)

接下来,删除 src 目录中的所有文件。

然后,在 src 目录中创建一个名为 index.js 的新文件,并添加以下代码

import ReactDOM from 'react-dom/client';
import App from './App.js';

const el = document.querySelector('#root');
const root = ReactDOM.createRoot(el);

root.render(<App />);Code language: JavaScript (javascript)

index.js 文件在屏幕上呈现 App 组件。

之后,在 src 目录中创建一个名为 App.js 的新文件,并使用以下代码

const App = () => {
  return <div>Wikipedia Search</div>;
};

export default App;Code language: JavaScript (javascript)

最后,通过在终端中运行以下命令来运行 React 应用程序

npm startCode language: JavaScript (javascript)

您将在 http://localhost:3000 的 Web 浏览器上看到新的 React 应用程序。

调用 API

从 React 应用程序调用 API 有多种方法。最简单的方法是使用 Web 浏览器提供的原生 Fetch API,因为我们不需要安装任何第三方包。

首先,通过传递 API 端点来调用 fetch() 方法

const response = await fetch(url);Code language: JavaScript (javascript)

其次,调用 Response 对象的 json() 方法来解析 JSON 正文内容

const results = await response.json();Code language: JavaScript (javascript)

第三,返回解析后的 JSON 数据

return results;Code language: JavaScript (javascript)

调用维基百科搜索 API

首先,在名为 api.jssrc 目录中创建一个新文件

其次,定义一个 search() 函数,该函数为指定的搜索词调用维基百科搜索 API 并返回文章数组

export const search = async (searchTerm) => {
  const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srsearch=${searchTerm}`;
  const response = await fetch(url);
  const results = await response.json();
  return results.query.search;
};
Code language: JavaScript (javascript)

它是如何工作的。

步骤 1. 定义一个接受搜索词的搜索函数

export const search = async (searchTerm) => {
   // ...
}Code language: JavaScript (javascript)

步骤 2. 通过将 API URL 与搜索词连接来构造 API 端点

const url = `https://en.wikipedia.org/w/api.php?action=query&list=search&prop=info|extracts&inprop=url&utf8=&format=json&origin=*&srlimit=10&srsearch=${searchTerm}`;Code language: JavaScript (javascript)

步骤 3. 返回来自 API 的 JSON 数据

const response = await fetch(url);
const results = await response.json();
return results.query.search;Code language: JavaScript (javascript)

搜索结果包括 pageidtitlesnippettitlesnippet 可能包含 HTML 标签。

要删除 HTML 标签,我们可以在名为 util.jssrc 目录中创建一个新文件,并将 stripHTML() 函数定义如下

export const stripHtml = (html) => {
  let div = document.createElement('div');
  div.innerHTML = html;
  return div.textContent;
};Code language: JavaScript (javascript)

我们将在 Article 组件中使用 stripHTML 来从 titlesnippet 中删除 HTML。

创建 React 组件

我们将为 React 应用程序创建组件。

App 组件

以下 App 组件包含 SearchBarArticleList 组件

import { useState } from 'react';
import { search } from './api';
import SearchBar from './components/SearchBar';
import ArticleList from './components/ArticleList';
import './App.css';
import logo from './wikipedia-logo.png';

const App = () => {
  const [articles, setArticles] = useState([]);

  const handleSearch = async (searchTerm) => {
    const results = await search(searchTerm);
    setArticles(results);
  };

  return (
    <>
      <header>
        <img src={logo} alt="wikipedia" />
        <h1>Wikipedia Search</h1>
        <SearchBar onSearch={handleSearch} />
      </header>
      <main id="searchResult">
        <ArticleList articles={articles} />
      </main>
    </>
  );
};

export default App;Code language: JavaScript (javascript)

它是如何工作的。

步骤 1. 从 react 库导入 useState 函数,因为 App 将保存一些状态。

import { useState } from 'react';Code language: JavaScript (javascript)

步骤 2. 从 api.js 模块导入 search 函数

import { search } from './api';Code language: JavaScript (javascript)

步骤 3. 导入 SearchBarArticleList 组件

import SearchBar from './components/SearchBar';
import ArticleList from './components/ArticleList';Code language: JavaScript (javascript)

步骤 4. 导入 App.css 和 wikipedia-logo.png 文件

import './App.css';
import logo from './wikipedia-logo.png';Code language: JavaScript (javascript)

步骤 5. 定义 App 组件

const App = () => {
   // ...
};Code language: JavaScript (javascript)

步骤 5. 定义一个状态 (articles),它是一个文章数组,并将其默认值初始化为空数组

const [articles, setArticles] = useState([]);Code language: JavaScript (javascript)

步骤 6. 定义一个 handleSearch() 函数,该函数调用 search 函数以获取搜索结果,并使用这些结果更新 articles 状态

const handleSearch = async (searchTerm) => {
    const results = await search(searchTerm);
    setArticles(results);
};Code language: JavaScript (javascript)

步骤 7. 返回包含徽标、标题、SearchBar 组件和 ArticleList 组件的 JSX。

return (
  <>
    <header>
      <img src={logo} alt="wikipedia" />
      <h1>Wikipedia Search</h1>
      <SearchBar onSearch={handleSearch} />
    </header>
    <main id="searchResult">
      <ArticleList articles={articles} />
    </main>
  </>
);Code language: JavaScript (javascript)

在 JSX 中

  • 将 handleSearch 函数传递给 SearchBar 组件的 onSearch 道具。
  • 将 articles 状态作为道具传递给 ArticleList 组件。

显示加载状态

当您调用 API 时,它需要一些时间才能返回结果,因此显示加载指示器对于改善用户体验至关重要。

为此,我们将一个新的状态变量 isLoading 添加到 App 组件中

const [isLoading, setIsLoading] = useState(false);Code language: JavaScript (javascript)

在调用搜索函数之前,我们将 isLoading 的值设置为 true,在获取结果后,我们将它的值设置为 false

const handleSearch = async (searchTerm) => {
    setIsLoading(true);
    const results = await search(searchTerm);
    setArticles(results);
    setIsLoading(false);
};Code language: JavaScript (javascript)

要显示加载指示器,我们将使用以下条件渲染

{isLoading && <p>Loading...</p>}Code language: HTML, XML (xml)

处理错误

在调用 API 时,可能会发生各种错误,例如网络断开连接或服务器中断。因此,在出现错误时向用户显示错误消息非常重要。

为了处理这个问题,我们可以在 App 组件中添加一个新的状态变量,并在发生错误时更新它

const [error, setError] = useState(null);Code language: JavaScript (javascript)

为了处理错误,我们可以使用 try…catch 语句。如果发生错误,我们可以更新错误状态变量

const handleSearch = async (searchTerm) => {
  setIsLoading(true);
  try {
    const results = await search(searchTerm);
    setArticles(results);
  } catch (err) {
    setError('Something went wrong. Please try again.');
  } finally {
    setIsLoading(false);
  }
};Code language: JavaScript (javascript)

在这段代码中,我们在 catch 块中将错误状态变量设置为错误消息。要呈现错误消息,我们可以使用条件渲染

{error && <p className="error">{error}</p>}Code language: HTML, XML (xml)

以下是完整的 App.js 组件

import { useState } from 'react';
import SearchBar from './components/SearchBar';
import ArticleList from './components/ArticleList';
import { search } from './api';
import './App.css';
import logo from './wikipedia-logo.png';

const App = () => {
  const [articles, setArticles] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const handleSearch = async (searchTerm) => {
    setIsLoading(true);
    try {
      const results = await search(searchTerm);
      setArticles(results);
    } catch (err) {
      setError('Something went wrong. Please try again.');
    } finally {
      setIsLoading(false);
    }
  };

  return (
    <>
      <header>
        <img src={logo} alt="wikipedia" />
        <h1>Wikipedia Search</h1>
        <SearchBar onSearch={handleSearch} />
      </header>

      <main id="searchResult">
        {isLoading && <p>Loading...</p>}
        {error && <p className="error">{error}</p>}
        <ArticleList articles={articles} />
      </main>
    </>
  );
};

export default App;Code language: JavaScript (javascript)

请注意,您可以考虑整合 AbortController 来中止先前的搜索请求,如果用户启动新的搜索请求

SearchBar 组件

SearchBar 组件将允许用户输入搜索词并运行一个函数来调用搜索的 API

import { useState } from 'react';

const SearchBar = () => {
  const [searchTerm, setSearchTerm] = useState('');

  const handleSubmit = (event) => {
    event.preventDefault();
    onSearch(searchTerm);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="search"
        name="searchTerm"
        id="searchTerm"
        placeholder="Enter a search term..."
        value={searchTerm}
        onChange={(event) => {
          setSearchTerm(event.target.value);
        }}
      />
    </form>
  );
};

export default SearchBar;Code language: JavaScript (javascript)

它是如何工作的。

步骤 1. 从 react 库导入 useState 函数

import { useState } from 'react';Code language: JavaScript (javascript)

步骤 2. 定义 SearchBar 组件,该组件接受函数 onSearch 作为道具

const SearchBar = ({ onSearch }) => {
   // ..
}Code language: JavaScript (javascript)

步骤 3. 为 SearchBar 组件定义一个 searchTerm 状态,并将其默认值初始化为空字符串

 const [searchTerm, setSearchTerm] = useState('');Code language: JavaScript (javascript)

步骤 4. 创建一个处理提交事件的事件处理程序

 const handleSubmit = (e) => {
    e.preventDefault();
    onSearch(searchTerm);
  };Code language: JavaScript (javascript)

在提交事件中,我们调用 e.preventDefault() 来阻止用户提交表单时整个页面重新加载,并使用 searchTerm 状态作为参数调用 onSearch 函数。

步骤 5. 返回包含表单和输入元素的 JSX

return (
    <form onSubmit={handleSubmit}>
      <input
        type="search"
        name="searchTerm"
        id="searchTerm"
        placeholder="Enter a search term..."
        value={searchTerm}
        onChange={(e) => {
          setSearchTerm(e.target.value);
        }}
      />
    </form>
  );Code language: JavaScript (javascript)

JSX

  • handleSubmit 事件处理程序连接到表单的 onSubmit 道具。
  • 在输入元素的更改事件处理程序中调用 setSearchTerm 来更新状态。e.target.value 返回输入元素的当前值。setSearchTerm 函数将为组件的 searchTerm 状态分配一个新的输入值。

步骤 6. 使用默认导出导出 SearchBar 组件

export default SearchBar;Code language: JavaScript (javascript)

ArticleList 组件

ArticleList 组件显示 Article 组件列表

import Article from './Article';

const ArticleList = ({ articles }) => {
  const renderedArticles = articles.map((article) => {
    return <Article key={article.pageid} article={article} />;
  });

  return <div>{renderedArticles}</div>;
};

export default ArticleList;Code language: JavaScript (javascript)

它是如何工作的。

步骤 1. 导入 Article 组件

import Article from './Article';Code language: JavaScript (javascript)

步骤 2. 定义 ArticleList 组件,该组件接受一个文章数组作为道具,并使用 Article 组件呈现每篇文章

const ArticleList = ({ articles }) => {
  const renderedArticles = articles.map((article) => {
    return <Article key={article.pageid} article={article} />;
  });

  return <div>{renderedArticles}</div>;
};Code language: JavaScript (javascript)

步骤 3. 将 ArticleList 组件作为默认组件导出

export default ArticleList;Code language: JavaScript (javascript)

Article 组件

Article 组件呈现一篇文章

import { stripHtml } from '../util';

const Article = ({ article }) => {
  const url = `https://en.wikipedia.org/?curid=${article.pageid}`;
  const title = stripHtml(article.title);
  const snippet = stripHtml(article.snippet);

  return (
    <article>
      <a href={url} title={title}>
        <h2>{title}</h2>
      </a>
      <div className="summary">{snippet}...</div>
    </article>
  );
};

export default Article;Code language: JavaScript (javascript)

它是如何工作的。

步骤 1. 从 util 库导入 stripHTML 函数

import { stripHtml } from '../util';Code language: JavaScript (javascript)

步骤 2. 创建一个 Article 组件,该组件呈现 article 道具

const Article = ({ article }) => {
  const url = `https://en.wikipedia.org/?curid=${article.pageid}`;
  const title = stripHtml(article.title);
  const snippet = stripHtml(article.snippet);

  return (
    <article>
      <a href={url} title={title}>
        <h2>{title}</h2>
      </a>
      <div className="summary">{snippet}...</div>
    </article>
  );
};Code language: JavaScript (javascript)

Article 组件中

  • 构造一个指向维基百科上文章的 URL。
  • 从文章对象的标题和摘要中删除 HTML 标签。
  • 返回 <article> JSX 元素。

步骤 3. 使用默认导出导出 Article 组件

export default Article;Code language: JavaScript (javascript)

下载 Wiki 搜索源代码。

总结

  • 使用原生浏览器 Fetch API 调用外部 API。
本教程是否有帮助?