摘要:在本教程中,您将学习使用 Web 浏览器提供的内置 Fetch API 在 React 应用程序中调用 API。
维基百科搜索 React 应用程序简介
我们将创建一个新的 允许您搜索维基百科文章的 React 应用程序。为此,React 应用程序需要调用维基百科搜索 API

React 应用程序将如下所示

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

以下是组件层次结构
在此组件层次结构中
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 应用程序
首先,在您的计算机上打开一个终端,并使用 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)您将在 https://: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.js 的 src 目录中创建一个新文件
其次,定义一个 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)搜索结果包括 pageid、title 和 snippet。title 和 snippet 可能包含 HTML 标签。
要删除 HTML 标签,我们可以在名为 util.js 的 src 目录中创建一个新文件,并将 stripHTML() 函数定义如下
export const stripHtml = (html) => {
let div = document.createElement('div');
div.innerHTML = html;
return div.textContent;
};Code language: JavaScript (javascript)我们将在 Article 组件中使用 stripHTML 来从 title 和 snippet 中删除 HTML。
创建 React 组件
我们将为 React 应用程序创建组件。
App 组件
以下 App 组件包含 SearchBar 和 ArticleList 组件
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. 导入 SearchBar 和 ArticleList 组件
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)总结
- 使用原生浏览器 Fetch API 调用外部 API。