How To Make An API Call In React

How To Make An API Call In React

How To Make An API Call In React

Lucas Allen
Author
Lucas Allen

There are different methods to do API calls in React, but the two most ubiquitous methods are the Fetch API (JavaScript interface for accessing and manipulating parts of the HTTP pipeline) and Axios ((a promised-based HTTP client for JavaScript).

For brevity, we will use Axios in this article to focus on what React offers when making an API request.

We will provide simple steps and basic code so that you may follow along. Making an API call in React offers many advantages in handling the different return states of an API request. Let’s get started.

Installing & running a new React App with Axios

Enter into an empty directory. We will start downloading some basic code here.

$ yarn create react-app API-call-example
$ cd API-call-example

Running the line above will let Yarn use create-react-app to create a brand new blank React application named “API-call-example” and take you into the directory where the code for the app lives.

Now we want to install Axios and start the app.

$ yarn add Axios && yarn start

This will start up your new React app. By default, your app will be running on localhost, port 3000: localhost:3000.

Setting up a basic structure

The codebase for our API-call-example application should have the following folder structure:

- API-call-example

- public
   # public assets live here such as HTML, CSS, favicons, etc

- src
   App.js
   index.js
   # among others, but the above are most important

package.json
README.md
 yarn.lock

Make a simple export for an Axios instance (this is pretty handy in general, as it allows you to later easily configure Axios for all your requests).

// src/axios.js
import Axios from 'Axios';

export default Axios.create({
 baseURL: "https://jsonplaceholder.typicode.com",
});

Let’s prepare the examples to follow and create a services folder to keep all our request methods:

$ mkdir src/services && touch src/services/posts.js && touch 
src/services/post.js

Now we should have the following:

- src
 - services
   post.js
   posts.js
// src/services/post.js
const getPost = async (postId) => {
try {
const post = await axios.get(`/posts/${postId}`);
return post;
 } catch (e) {
const msg = e?.response?.error.message ?? e?.message ?? 'Unknown Error';
console.error(msg);

return false;
 }
};

export default getPost;

// src/services/posts
import axios from '../axios';

const getPosts = async () => {
try {
const post = await axios.get(`/posts`);
return post;
 } catch (e) {
const msg = e?.response?.error.message ?? e?.message ?? 'Unknown Error';
console.error(msg);

return false;
 }
};

export default getPosts;

Finally, we’re ready to put these services to work in the React app.

Making the API call with useEffect

Sometimes we would like to request data as soon as the component renders for the first time. In these cases, we can do the following.

const getAllPosts = async () => {
 setLoadingPosts(true);
const response = await getPosts();
 setLoadingPosts(false);

if (!response) return;

const { data: postItems } = response;
if (postItems && postItems.length) {
   setPosts(postItems);
 }
};

useEffect(( ) => {
 getAllPosts( );
}, [ ]);

Note how we extracted the logic for retrieving the data in a separate function. Not only is this a good practice generally, but this is a necessity when using async/await inside a useEffect call since the useEffect function cannot be asynchronous itself.

Also note the second argument in useEffect: [ ]. These brackets represent the dependency array for useEffect and tell React to run the function in the useEffect’s first argument whenever anything inside the array changes.

In this case, since the array is empty, React will only run the useEffect function once.

See the code below, but try to first implement a component that uses this method to retrieve data along with a loading state and a failure state.

// src/components/PostsIndex.js
import { useEffect, useState } from 'react';
import getPosts from '../services/posts';

const PostsIndex = () => {
const [loadingPosts, setLoadingPosts] = useState(false);
const [posts, setPosts] = useState();

const getAllPosts = async () => {
   setLoadingPosts(true);
const response = await getPosts();
   setLoadingPosts(false);

if (!response) return;

const { data: postItems } = response;
if (postItems && postItems.length) {
     setPosts(postItems);
   }
 };

 useEffect(() => {
   getAllPosts();
 }, []);

return (
<div style={{ padding: '1rem', textAlign: 'left' }}>
<header>Posts:</header>
     {loadingPosts ? (
<p>Loading posts...</p>
     ) : posts?.length ? (
<ul>
         {posts?.slice(0,5).map((post) => (
<li key={post.id}>{ post.title }</li>
         ))}
</ul>
     ) : (
<p>No posts found!</p>
     )}
</div>
 );
};

export default PostsIndex;

Making the API call from a click event

Sometimes you may want to request data after a user takes a specific action, such as clicking a button. Consider the following method:

const handleGetPost = async () => {   
const id = postIdRef.current?.value;
if (!id) return;                    

 setLoadingPost(true);               
const response = await getPost(id);
 setLoadingPost(false);              
if (!response) return;              

const { data: postItem } = response;
if (postItem) {                     
   setPost(postItem);                
 }                                   
};

How might one use an HTML button element to call this function? First, we need an input to let the user type the id of the post, then we need to store or retrieve that value. In this case, we can use simple text input and useRef to store an imperative reference to that input.

The button and the input elements might look something like this:

<input                                          
 name='postId'
 ref={(inputEl) => postIdRef.current = inputEl}
 style={{ marginRight: '1rem' }}               
 type='text'
/>                          

<button
 disabled={loadingPost}
 onClick={handleGetPost}
 style={{ marginBottom: '1rem' }}              
>
 { loadingPost ? 'Loading' : 'Get Post!' }
</button>

Once you have the data you need, you should be able to display it. Putting it all together:

// src/components/PostGetter.js
import { useRef, useState } from 'react';                       
import getPost from '../services/post';                         

const PostGetter = () => {                                      
const [loadingPost, setLoadingPost] = useState(false);        
const [post, setPost] = useState();                           
const postIdRef = useRef();                                   

const handleGetPost = async () => {                           
const id = postIdRef.current?.value;                        
if (!id) return;                                            

   setLoadingPost(true);                                       
const response = await getPost(id);                         
   setLoadingPost(false);                                      
if (!response) return;                                      

const { data: postItem } = response;                        
if (postItem) {                                             
     setPost(postItem);                                        
   }                                                           
 };                                                            

return (                                                      
<div style={{ padding: '1rem', textAlign: 'left' }}>
<input
       name='postId'
       ref={(inputEl) => postIdRef.current = inputEl}          
       style={{ marginRight: '1rem' }}                         
       type='text'                                             
     />                                                        

<button
       disabled={loadingPost}
       onClick={handleGetPost}
       style={{ marginBottom: '1rem' }}                        
     >
       { loadingPost ? 'Loading' : 'Get Post!' }               
</button>

     { post !== undefined && (                                 
<div style={{ maxWidth: '40rem', width: '100%' }}>
<header>POST: <strong>{ post.title }</strong></header>

<ul style={{ textAlign: 'left' }}>
<li>{ post.id }</li>
<li>{ post.userId }</li>
<li>{ post.body }</li>
</ul>
</div>
     )}                            
</div>
 );                                
};                                  

export default PostGetter;

In the above example app, we have illustrated how to organize the logic that retrieves data via an API request and how those functions might be used in a React application either with a useEffect hook or via an event handler.