Summary: in this tutorial, you will learn to call API in a React app using the built-in Fetch API provided by web browsers.
Introduction to Wikipedia Search React App
We’ll create a new React app that allows you to search Wikipedia articles. To do that, the React app needs to call Wikipedia Search API:
The React app will look like the following:
We can break down this React app into the following components:
Here’s the component hierarchy:
In this component hierarchy:
- The
App
component is a parent component that includes other components. - The
SearchBar
component allows you to type a search term and press the Enter key to submit it. - The
ArticleList
component is responsible for rendering the search results, which include a list of articles. - The
Article
component renders each article in the search results.
Props and State Design
There are some questions we need to answer before creating the app:
- What is the state of the application?
- Which component should handle the API call?
- How should we design the props system?
The app displays a list of articles so it should have an array of articles as a state variable:
const [articles, setArticles] = useState([]);
Code language: JavaScript (javascript)
Since the App
component uses the ArticleList
component to render the article list, it should pass the articles
array as a prop to the ArticleList
.
Additionally, the ArticleList
should pass each article
as a prop to the Article
component for rendering each article.
If we call Wikipedia API in the SearchBar
component, we can get a list of articles. But how do we pass the article list from the SearchBar
component to the App
component?
Typically, React allows us to pass props from parent components to the child components, not from the child component to the parent component or between sibling components.
To overcome this limitation, we can pass a callback function from the App
component to the SearchBar
component as a prop.
When the user submits the search form, we can call the callback function onSearch
to update the articles
state of the App
component:
Create a new React app
First, open a terminal on your computer and create a new React app using the create-react-app
command:
npx create-react-app wiki-search
Code language: JavaScript (javascript)
Next, delete all files in the src
directory.
Then, create a new index.js
file in the src
directory and add the following code:
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)
The index.js
file renders the App
component on the screen.
After that, create a new file named App.js
in the src
directory with the following code:
const App = () => {
return <div>Wikipedia Search</div>;
};
export default App;
Code language: JavaScript (javascript)
Finally, run the React app by running the following command in the terminal:
npm start
Code language: JavaScript (javascript)
You’ll see the new React app on the web browser at http://localhost:3000
.
Calling an API
There are several ways to call an API from a React app. The simplest one is to use the native Fetch API provided by the web browser because we don’t need to install any third-party package.
First, call the fetch()
method by passing the API endpoint:
const response = await fetch(url);
Code language: JavaScript (javascript)
Second, call the json()
method of the Response
object to parse the JSON body contents:
const results = await response.json();
Code language: JavaScript (javascript)
Third, return the parsed JSON data:
return results;
Code language: JavaScript (javascript)
Calling Wikipedia Search API
First, create a new file in the src
directory named api.js
:
Second, define a search()
function that calls Wikipedia Search API for a specified search term and returns an array of articles:
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)
How it works.
Step 1. Define a search function that accepts a search term:
export const search = async (searchTerm) => {
// ...
}
Code language: JavaScript (javascript)
Step 2. Construct the API endpoint by concatenating the API URL with the search term:
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)
Step 3. Return JSON data from the API:
const response = await fetch(url);
const results = await response.json();
return results.query.search;
Code language: JavaScript (javascript)
The search results include pageid
, title
, and snippet
. The title
and snippet
may contain HTML tags.
To strip the HTML tags, we can create a new file util.js
in the src
directory and define the stripHTML()
function as follows:
export const stripHtml = (html) => {
let div = document.createElement('div');
div.innerHTML = html;
return div.textContent;
};
Code language: JavaScript (javascript)
We’ll use the stripHTML
in the Article
component to strip HTML from the title
and snippet
.
Create React components
We’ll create the components for the React app.
App component
The following App
component includes the SearchBar
and ArticleList
components:
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)
How it works.
Step 1. Import the useState
function from the react library because the App
will hold some piece of state.
import { useState } from 'react';
Code language: JavaScript (javascript)
Step 2. Import the search
function from the api.js
module:
import { search } from './api';
Code language: JavaScript (javascript)
Step 3. Import the SearchBar
and ArticleList
components:
import SearchBar from './components/SearchBar';
import ArticleList from './components/ArticleList';
Code language: JavaScript (javascript)
Step 4. Import the App.css and wikipedia-logo.png
files:
import './App.css';
import logo from './wikipedia-logo.png';
Code language: JavaScript (javascript)
Step 5. Define the App
component:
const App = () => {
// ...
};
Code language: JavaScript (javascript)
Step 5. Define a piece of state (articles
) which is an array of articles, and initialize its default value to an empty array:
const [articles, setArticles] = useState([]);
Code language: JavaScript (javascript)
Step 6. Define a handleSearch()
function that calls the search
function to get the search results and update the articles
state with these results:
const handleSearch = async (searchTerm) => {
const results = await search(searchTerm);
setArticles(results);
};
Code language: JavaScript (javascript)
Step 7. Return JSX that includes a logo, a heading, a SearchBar component, and an ArticleList component.
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)
In the JSX:
- Pass the handleSearch function to the onSearch prop of the SearchBar component.
- Pass the articles state as a prop to the ArticleList component.
Showing the loading state
When you call an API, it takes some time to get the result back, so showing a loading indicator is important for a better user experience.
To do that, we add a new state variable isLoading
to the App
component:
const [isLoading, setIsLoading] = useState(false);
Code language: JavaScript (javascript)
Before calling the search function, we set the value of isLoading
to true
and after getting the result we set its value to false
:
const handleSearch = async (searchTerm) => {
setIsLoading(true);
const results = await search(searchTerm);
setArticles(results);
setIsLoading(false);
};
Code language: JavaScript (javascript)
To show the loading indicator, we use a conditional rendering as follows:
{isLoading && <p>Loading...</p>}
Code language: HTML, XML (xml)
Handling errors
When calling an API, various errors may occur such as a network disconnect or server outage. That’s why it’s important to show users an error message when something goes wrong.
To handle this, we can add a new state variable to the App component and update it whenever an error occurs:
const [error, setError] = useState(null);
Code language: JavaScript (javascript)
To handle errors, we can use the try…catch statement. If an error occurs, we can update the error state variable:
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)
In this code, we set the error state variable to an error message in the catch
block. To render the error message, we can use a conditional rendering:
{error && <p className="error">{error}</p>}
Code language: HTML, XML (xml)
Here’s the complete App.js
component:
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)
Note that you may consider incorporating the AbortController to abort the previous search request if users start a new one
SearchBar component
The SearchBar
component will allow users to enter a search term and run a function to call the API for searching:
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)
How it works.
Step 1. Import the useState
function from the react library:
import { useState } from 'react';
Code language: JavaScript (javascript)
Step 2. Define the SearchBar
component that accepts a function onSearch
as a prop:
const SearchBar = ({ onSearch }) => {
// ..
}
Code language: JavaScript (javascript)
Step 3. Define a searchTerm
state for the SearchBar
component and initialize its default value to an empty string:
const [searchTerm, setSearchTerm] = useState('');
Code language: JavaScript (javascript)
Step 4. Create an event handler that handles the submit event:
const handleSubmit = (e) => {
e.preventDefault();
onSearch(searchTerm);
};
Code language: JavaScript (javascript)
In the submit event, we call the e.preventDefault()
to prevent the whole page from reloading when users submit the form and call the onSearch
function with the searchTerm
state as the argument.
Step 5. Return JSX that includes a form and an input element:
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)
In the JSX
:
- Wire the
handleSubmit
event handler to theonSubmit
prop of the form. - Call the
setSearchTerm
to update the state in the change event handler of the input element. Thee.target
.value returns the current value of the input element. ThesetSearchTerm
function will assign a new input value to thesearchTerm
state of the component.
Step 6. Export the SearchBar
component using a default export:
export default SearchBar;
Code language: JavaScript (javascript)
ArticleList component
The ArticleList
component displays a list of Article
components:
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)
How it works.
Step 1. Import the Article component:
import Article from './Article';
Code language: JavaScript (javascript)
Step 2. Define the ArticleList
component that accepts an array of articles as a prop and renders each article using the Article component:
const ArticleList = ({ articles }) => {
const renderedArticles = articles.map((article) => {
return <Article key={article.pageid} article={article} />;
});
return <div>{renderedArticles}</div>;
};
Code language: JavaScript (javascript)
Step 3. Export the ArticleList
component as a default component:
export default ArticleList;
Code language: JavaScript (javascript)
Article component
The Article
component renders an 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)
How it works.
Step 1. Import the stripHTML
function from the util
library:
import { stripHtml } from '../util';
Code language: JavaScript (javascript)
Step 2. Create an Article
component that renders the article
prop:
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)
In the Article
component:
- Construct a URL to an article on Wikipedia.
- Strip HTML tags from the title and snippet of the article object.
- Return the
<article>
JSX element.
Step 3. Export the Article
component as a default export:
export default Article;
Code language: JavaScript (javascript)
Download the Wiki Search source code.
Summary
- Use native browser Fetch API to call an external API.