摘要:在本教程中,您将学习使用 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-search
Code 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 start
Code 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.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。