React JS APIs Interview Questions

React JS APIs Interview Questions

On May 25, 2025, Posted by , In Reactjs, With Comments Off on React JS APIs Interview Questions

Table Of Contents

As a developer, mastering React JS APIs is crucial for building dynamic and scalable applications. In interviews, recruiters often dive deep into your understanding of APIs like useState, useEffect, Context API, and advanced concepts like Error Boundaries and Suspense. They don’t just test your knowledge—they evaluate how you apply these APIs to solve real-world problems, optimize performance, and manage complex state. Whether it’s explaining how React’s lifecycle methods work or handling challenging scenarios with hooks, these questions are designed to separate good developers from great ones.

In this guide, I’ve compiled a collection of React JS APIs interview questions to help you prepare like a pro. Each question is paired with insightful answers and practical examples to strengthen your knowledge and application skills. Whether you’re aiming for a junior role or a senior position, this content will empower you to confidently tackle any API-related question. By the time you finish, you’ll be ready to impress your interviewer and land your dream role in the React ecosystem. Let’s dive in and get you interview-ready!

1. How do you fetch data from an API in a React application using fetch?

In my experience, fetching data from an API in React is straightforward with the fetch API. I typically use the fetch function inside a lifecycle method or React Hook like useEffect to ensure the data is fetched when the component renders. The fetch API returns a promise, so I use .then() to handle the response and .catch() to handle errors. This approach keeps the process organized and ensures I can manipulate the fetched data efficiently.

Here’s an example of how I fetch data and store it in the component state:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch('https://api.example.com/items')
      .then(response => response.json())
      .then(data => setData(data))
      .catch(error => console.error('Error fetching data:', error));
  }, []);

  return (
    <div>
      <h1>Fetched Data</h1>
      <ul>
        {data.map((item, index) => (
          <li key={index}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}
export default App;

In this code, I use the useEffect hook to fetch data when the component mounts. The fetch function retrieves the data, converts it into JSON, and updates the state using setData. I also include error handling to log any issues to the console.

See also: React JS Interview Questions for 5 years Experience

2. How would you handle asynchronous data fetching in React using useEffect?

When I handle asynchronous data fetching in React, I rely on the useEffect hook since it runs after the component renders. To ensure the best practices, I use async/await for cleaner and more readable code. However, because useEffect doesn’t directly accept an asynchronous function, I define the async function inside the hook and call it explicitly. This allows me to manage the asynchronous operations properly without causing side effects.

Here’s an example of how I handle asynchronous data fetching using useEffect:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/items');
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };
    fetchData();
  }, []);

  return <div>Data fetched asynchronously</div>;
}
export default App;

In this example, I use an async function fetchData to fetch and process the data. Wrapping it in try/catch allows me to manage errors gracefully. Using await ensures I get the resolved data before updating the state.

3. How can you manage loading states while fetching data?

Managing loading states is crucial for improving user experience while fetching data. I typically use a separate state variable, like isLoading, to track whether the data is being loaded. By setting isLoading to true before starting the fetch and false once the fetch is complete, I can display a loading indicator to users. This ensures they know the app is working on fetching the data.

Here’s an example of how I manage loading states:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    fetch('https://api.example.com/items')
      .then(response => response.json())
      .then(data => {
        setData(data);
        setIsLoading(false);
      })
      .catch(error => {
        console.error('Error fetching data:', error);
        setIsLoading(false);
      });
  }, []);

  return isLoading ? <p>Loading...</p> : <ul>{data.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}
export default App;

In this example, I use isLoading to conditionally render a “Loading…” message while data is being fetched. Once the data is available or an error occurs, I update isLoading to false to render the actual content.

See also: Amazon Angular JS interview Questions

4. How do you handle error states when fetching data from an API?

In my experience, handling error states ensures the application behaves predictably, even when things go wrong. I use a separate state variable, like error, to capture any issues during the fetch operation. By updating the error state when an issue occurs, I can display a user-friendly error message instead of leaving users guessing.

Here’s an example of how I manage error states:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [error, setError] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/items')
      .then(response => {
        if (!response.ok) throw new Error('Network response was not ok');
        return response.json();
      })
      .then(data => setData(data))
      .catch(error => setError(error.message));
  }, []);

  return (
    <div>
      {error ? <p>Error: {error}</p> : <ul>{data.map(item => <li key={item.id}>{item.name}</li>)}</ul>}
    </div>
  );
}
export default App;

In this code, I use setError to update the error message when a fetch fails. If there’s an error, I render the error message instead of the fetched data. This approach keeps the app responsive and user-friendly.

See also: Deloitte Senior Developer Interview Questions

5. How can you implement pagination for fetched data in React?

Implementing pagination helps in managing large datasets efficiently by loading smaller chunks of data. I typically use query parameters in the API URL, like ?page=1, to fetch specific pages of data. I also maintain a currentPage state to track which page the user is viewing. This, combined with navigation buttons, allows users to seamlessly switch between pages.

Here’s an example of implementing pagination:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [currentPage, setCurrentPage] = useState(1);

  useEffect(() => {
    fetch(`https://api.example.com/items?page=${currentPage}`)
      .then(response => response.json())
      .then(data => setData(data))
      .catch(error => console.error('Error fetching data:', error));
  }, [currentPage]);

  return (
    <div>
      <ul>{data.map(item => <li key={item.id}>{item.name}</li>)}</ul>
      <button onClick={() => setCurrentPage(prev => Math.max(prev - 1, 1))}>Previous</button>
      <button onClick={() => setCurrentPage(prev => prev + 1)}>Next</button>
    </div>
  );
}
export default App;

In this example, I use currentPage to determine the page to fetch. Clicking the “Previous” or “Next” button updates the page number, triggering a new fetch with the updated currentPage. This approach ensures efficient data management and navigation.

6. How do you use axios for data fetching in React?

In my experience, axios is a popular library for data fetching because of its simplicity and rich features. Unlike the fetch API, axios automatically handles JSON conversion and has built-in support for interceptors and timeouts. To use axios, I import the library and call its methods, such as axios.get, to fetch data. I usually use it within a useEffect hook to fetch data when the component renders.

Here’s an example of fetching data with axios:

import React, { useEffect, useState } from 'react';
import axios from 'axios';

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await axios.get('https://api.example.com/items');
        setData(response.data);
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };
    fetchData();
  }, []);

  return <ul>{data.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}
export default App;

In this code, I use axios.get to fetch data from the API and handle errors gracefully using a try-catch block. The response data is set into the state and displayed as a list.

7. What is the difference between using fetch and axios in React?

In my experience, both fetch and axios are used for API requests, but axios provides more features out of the box. For example, axios supports request cancellation, default headers, and interceptors, while fetch requires additional setup for such capabilities.

Here’s an example comparison:

fetch('https://api.example.com/items')
  .then(response => {
    if (!response.ok) throw new Error('Network error');
    return response.json();
  })
  .then(data => console.log(data))
  .catch(error => console.error(error));

With axios, the process is simpler:

axios.get('https://api.example.com/items')
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

While fetch is lightweight, I prefer axios for its ease of use and advanced features.

See also: Tech Mahindra React JS Interview Questions

8. How would you implement infinite scrolling in a React application?

In my projects, I implement infinite scrolling by detecting when the user reaches the bottom of the page and then loading more data. I use the IntersectionObserver API or scroll event listeners to trigger API calls dynamically. The useState hook tracks the current data, while useEffect handles data fetching and updates the state.

Here’s an example using IntersectionObserver:

import React, { useState, useEffect, useRef } from 'react';

function App() {
  const [data, setData] = useState([]);
  const [page, setPage] = useState(1);
  const loader = useRef(null);

  useEffect(() => {
    const fetchData = async () => {
      const response = await fetch(`https://api.example.com/items?page=${page}`);
      const result = await response.json();
      setData(prev => [...prev, ...result]);
    };
    fetchData();
  }, [page]);

  useEffect(() => {
    const observer = new IntersectionObserver(
      entries => entries[0].isIntersecting && setPage(prev => prev + 1),
      { threshold: 1 }
    );
    if (loader.current) observer.observe(loader.current);
    return () => observer.disconnect();
  }, []);

  return (
    <div>
      {data.map((item, index) => <p key={index}>{item.name}</p>)}
      <div ref={loader}>Loading...</div>
    </div>
  );
}
export default App;

Here, I dynamically load more data as the loader div enters the viewport, creating a seamless scrolling experience.

See also: Tech Mahindra React JS Interview Questions

9. How can you use React Query to handle data fetching?

React Query, in my opinion, is a powerful tool for managing server state in React. It simplifies data fetching, caching, and synchronizing server data with your UI. I typically use the useQuery hook to fetch data, which automatically caches the result and refetches when needed. This eliminates the need for manual state management or caching logic.

Here’s an example using React Query:

import React from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';

function App() {
  const { data, error, isLoading } = useQuery('items', async () => {
    const response = await axios.get('https://api.example.com/items');
    return response.data;
  });

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>Data Fetched with React Query</h1>
      <ul>{data.map(item => <li key={item.id}>{item.name}</li>)}</ul>
    </div>
  );
}
export default App;

Using useQuery, I don’t have to worry about caching or refetching, as React Query handles it all automatically.

10. How do you handle caching for API responses in React?

For caching, I often use libraries like React Query or localStorage for simpler cases. React Query is excellent for managing dynamic data, as it automatically caches responses and invalidates stale data. For static or rarely updated data, I prefer storing it in localStorage or sessionStorage, allowing quick access without redundant API calls.

Here’s an example of using localStorage for caching:

useEffect(() => {
  const fetchData = async () => {
    const cachedData = localStorage.getItem('items');
    if (cachedData) {
      setData(JSON.parse(cachedData));
    } else {
      const response = await fetch('https://api.example.com/items');
      const result = await response.json();
      localStorage.setItem('items', JSON.stringify(result));
      setData(result);
    }
  };
  fetchData();
}, []);

In this example, I check if data exists in localStorage before making a network request, reducing unnecessary calls and improving performance.

See also: Amazon React JS Interview Questions

11. How can you cancel an API request if a component unmounts?

In my experience, canceling an API request is essential to avoid memory leaks, especially when a component unmounts. I often use AbortController with the fetch API or the cancellation feature of axios. AbortController allows me to associate an API call with a signal that can terminate the request if the component unmounts.

Here’s an example using AbortController:

import React, { useEffect } from 'react';

function App() {
  useEffect(() => {
    const controller = new AbortController();
    const fetchData = async () => {
      try {
        const response = await fetch('https://api.example.com/items', {
          signal: controller.signal,
        });
        const data = await response.json();
        console.log(data);
      } catch (error) {
        if (error.name === 'AbortError') {
          console.log('Request canceled');
        } else {
          console.error(error);
        }
      }
    };
    fetchData();

    return () => controller.abort();
  }, []);

  return <div>Check the console for API data</div>;
}
export default App;

The controller.abort() method cancels the request during component unmount, ensuring no unnecessary processing.

12. How do you handle parallel API calls in React?

When I need to handle parallel API calls, I use Promise.all. This method lets me execute multiple requests simultaneously and waits for all of them to resolve. It’s particularly useful for fetching data from multiple endpoints.

Here’s how I manage parallel calls:

import React, { useState, useEffect } from 'react';

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const [users, posts] = await Promise.all([
          fetch('https://api.example.com/users').then(res => res.json()),
          fetch('https://api.example.com/posts').then(res => res.json()),
        ]);
        setData({ users, posts });
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };
    fetchData();
  }, []);

  return (
    <div>
      <h1>Users and Posts</h1>
      <h2>Users</h2>
      <ul>{data.users?.map(user => <li key={user.id}>{user.name}</li>)}</ul>
      <h2>Posts</h2>
      <ul>{data.posts?.map(post => <li key={post.id}>{post.title}</li>)}</ul>
    </div>
  );
}
export default App;

Here, I fetch users and posts simultaneously, improving efficiency and reducing wait times.

See also: React Redux Interview Questions And Answers

13. How can you make chained API requests (one request depends on the result of another)?

Chained API requests are necessary when the second request depends on data from the first. In my experience, I use async/await to make the process more readable and manageable.

Here’s an example of chained API requests:

import React, { useEffect, useState } from 'react';

function App() {
  const [userDetails, setUserDetails] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const userResponse = await fetch('https://api.example.com/user/1');
        const userData = await userResponse.json();
        const postsResponse = await fetch(`https://api.example.com/posts?userId=${userData.id}`);
        const postsData = await postsResponse.json();
        setUserDetails({ user: userData, posts: postsData });
      } catch (error) {
        console.error('Error fetching data:', error);
      }
    };
    fetchData();
  }, []);

  return (
    <div>
      {userDetails && (
        <>
          <h1>{userDetails.user.name}</h1>
          <h2>Posts:</h2>
          <ul>{userDetails.posts.map(post => <li key={post.id}>{post.title}</li>)}</ul>
        </>
      )}
    </div>
  );
}
export default App;

Here, I first fetch user details and then use the user ID to fetch their posts, creating a dependency chain.

14. How would you use GraphQL with React for data fetching?

In my projects, I use Apollo Client for working with GraphQL, as it simplifies querying and managing data. GraphQL allows me to fetch only the fields I need, reducing over-fetching or under-fetching issues common in REST APIs. With Apollo, I use the useQuery hook to execute GraphQL queries directly in React components.

Here’s an example:

import React from 'react';
import { useQuery, gql } from '@apollo/client';

const GET_USERS = gql`
  query GetUsers {
    users {
      id
      name
      email
    }
  }
`;

function App() {
  const { data, loading, error } = useQuery(GET_USERS);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>Users</h1>
      <ul>{data.users.map(user => <li key={user.id}>{user.name}</li>)}</ul>
    </div>
  );
}
export default App;

Here, I define a GraphQL query to fetch user data and use Apollo’s useQuery hook to execute and manage it effortlessly.

See more: TCS AngularJS Developer Interview Questions

15. How can you handle file uploads to a server in React?

When handling file uploads, I typically use a combination of the <input type="file"> element and FormData. The FormData object allows me to append the file and other data into a request body for the server. Using axios or fetch, I then send the file to the server endpoint.

Here’s an example using axios:

import React, { useState } from 'react';
import axios from 'axios';

function App() {
  const [file, setFile] = useState(null);

  const handleFileChange = e => setFile(e.target.files[0]);
  const handleUpload = async () => {
    const formData = new FormData();
    formData.append('file', file);
    try {
      const response = await axios.post('https://api.example.com/upload', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
      });
      console.log('Upload successful:', response.data);
    } catch (error) {
      console.error('Upload failed:', error);
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
}
export default App;

In this example, I use FormData to encapsulate the file data and send it securely to the server for processing.

16. How do you manage debouncing when making API calls (e.g., search functionality)?

In my experience, debouncing is essential when handling frequent user inputs, like search functionality, to reduce the number of API calls. I use libraries like lodash or implement a custom debounce function to delay API calls until the user stops typing.

Here’s an example using lodash:

import React, { useState, useCallback } from 'react';
import _ from 'lodash';

function Search() {
  const [query, setQuery] = useState('');

  const debounceSearch = useCallback(
    _.debounce(async (text) => {
      const response = await fetch(`https://api.example.com/search?q=${text}`);
      const data = await response.json();
      console.log(data);
    }, 500),
    []
  );

  const handleChange = (e) => {
    const value = e.target.value;
    setQuery(value);
    debounceSearch(value);
  };

  return <input type="text" value={query} onChange={handleChange} />;
}
export default Search;

In this code, I use lodash‘s debounce function to delay the API call by 500ms after the last keystroke. This prevents excessive API requests while the user types.

17. How can you handle authentication tokens while making API requests?

I use authentication tokens like JWT to secure API calls. Typically, I store the token in localStorage or sessionStorage and include it in the Authorization header of every request. Using axios simplifies this process by allowing me to set global headers.

Here’s an example:

import axios from 'axios';

axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = `Bearer ${localStorage.getItem('authToken')}`;

async function fetchData() {
  try {
    const response = await axios.get('/protected-resource');
    console.log(response.data);
  } catch (error) {
    console.error('Error fetching data:', error);
  }
}
fetchData();

In this example, I set the Authorization header globally in axios with the token stored in localStorage. This ensures all requests are authenticated without needing manual configuration each time.

18. How do you deal with cross-origin resource sharing (CORS) issues in React?

When I face CORS issues, I address them by setting up a proxy server or ensuring the server includes appropriate Access-Control-Allow-Origin headers. A common approach in development is using the proxy setting in the React app’s package.json.

Here’s an example:

{
  "proxy": "https://api.example.com"
}

In the example, adding a proxy configuration allows the React app to redirect API requests to the backend server during development, bypassing CORS restrictions. In production, the server must properly configure CORS headers.

19. How can you set up mock APIs for testing or development in React?

I often use tools like json-server or libraries like msw to create mock APIs for testing. These tools provide endpoints that mimic real APIs, allowing me to test API calls without relying on a live server.

Here’s an example using json-server:

// db.json
{
  "posts": [
    { "id": 1, "title": "Hello World" },
    { "id": 2, "title": "React is Awesome" }
  ]
}

Run the mock server with: json-server --watch db.json --port 3001. In this setup, I fetch data from http://localhost:3001/posts, simulating real API responses for testing without needing a live backend.

20. How do you implement server-side rendering (SSR) with data fetching in React?

For SSR, I use frameworks like Next.js, which provides seamless server-side data fetching capabilities. The getServerSideProps function in Next.js fetches data on the server and sends it to the client during the initial render.

Here’s an example:

import React from 'react';

export async function getServerSideProps() {
  const res = await fetch('https://api.example.com/items');
  const data = await res.json();
  return { props: { items: data } };
}

function HomePage({ items }) {
  return <ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}
export default HomePage;

In this code, getServerSideProps runs on the server to fetch data before rendering the component. The fetched data is passed as props, ensuring the client receives pre-rendered HTML with content.

Conclusion

Mastering React JS APIs is not just a technical requirement; it’s the backbone of building modern, dynamic applications that interact seamlessly with external services. As developers, the ability to handle data fetching, manage loading states, and ensure smooth API integrations directly impacts the performance and user experience of our apps. By diving into the essential techniques of error handling, authentication token management, and debouncing, you’ll gain the confidence to solve complex challenges and qrobust applications that scale efficiently.

With the insights and examples shared here, you are equipped to confidently tackle any React API-related question, whether it’s during an interview or in real-world development scenarios. By mastering these concepts, you’ll not only impress interviewers but also enhance your ability to build production-ready apps. Understanding how to manage asynchronous tasks, optimize performance, and integrate APIs effectively will give you a competitive edge, making you a valuable asset in any React development team.

Comments are closed.