React API Call

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:

react api call - wikipedia search app

The React app will look like the following:

react api call - wikipedia search app prototype

We can break down this React app into the following components:

react api call - wikipedia search

Here’s the component hierarchy:

Image

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([]);

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.

Image

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:

react api call - states & props design

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

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 />);

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;

Finally, run the React app by running the following command in the terminal:

npm start

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);

Second, call the json() method of the Response object to parse the JSON body contents:

const results = await response.json();

Third, return the parsed JSON data:

return results;

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;
};

How it works.

Step 1. Define a search function that accepts a search term:

export const search = async (searchTerm) => {
   // ...
}

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}`;

Step 3. Return JSON data from the API:

const response = await fetch(url);
const results = await response.json();
return results.query.search;

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;
};

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;

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';

Step 2. Import the search function from the api.js module:

import { search } from './api';

Step 3. Import the SearchBar and ArticleList components:

import SearchBar from './components/SearchBar';
import ArticleList from './components/ArticleList';

Step 4. Import the App.css and wikipedia-logo.png files:

import './App.css';
import logo from './wikipedia-logo.png';

Step 5. Define the App component:

const App = () => {
   // ...
};

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([]);

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);
};

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>
  </>
);

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);

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);
};

To show the loading indicator, we use a conditional rendering as follows:

{isLoading && <p>Loading...</p>}

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);

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);
  }
};

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>}

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;

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;

How it works.

Step 1. Import the useState function from the react library:

import { useState } from 'react';

Step 2. Define the SearchBar component that accepts a function onSearch as a prop:

const SearchBar = ({ onSearch }) => {
   // ..
}

Step 3. Define a searchTerm state for the SearchBar component and initialize its default value to an empty string:

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

Step 4. Create an event handler that handles the submit event:

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

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>
  );

In the JSX:

  • Wire the handleSubmit event handler to the onSubmit prop of the form.
  • Call the setSearchTerm to update the state in the change event handler of the input element. The e.target.value returns the current value of the input element. The setSearchTerm function will assign a new input value to the searchTerm state of the component.

Step 6. Export the SearchBar component using a default export:

export default SearchBar;

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;

How it works.

Step 1. Import the Article component:

import Article from './Article';

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>;
};

Step 3. Export the ArticleList component as a default component:

export default ArticleList;

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;

How it works.

Step 1. Import the stripHTML function from the util library:

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

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>
  );
};

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;

Download the Wiki Search source code.

Summary #

  • Use native browser Fetch API to call an external API.

Was this helpful?