0
0
ReactDebug / FixBeginner · 4 min read

How to Handle API Response in React: Simple Fix and Best Practices

In React, handle API responses by using useEffect to fetch data asynchronously and useState to store the response. Use async/await inside useEffect and update state to trigger re-render with the fetched data.
🔍

Why This Happens

Many beginners try to fetch API data directly inside the component body or forget to handle asynchronous calls properly. This causes errors like infinite loops or no data rendering because React re-renders before data arrives.

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

function UserList() {
  const [users, setUsers] = useState([]);

  // Incorrect: fetch called directly in component body
  fetch('https://jsonplaceholder.typicode.com/users')
    .then(response => response.json())
    .then(data => setUsers(data));

  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}
Output
Error: Too many re-renders. React limits the number of renders to prevent infinite loops.
🔧

The Fix

Move the API call inside useEffect to run it only once after the component mounts. Use async/await inside useEffect to handle the promise cleanly. Update state with the fetched data to trigger a re-render.

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

function UserList() {
  const [users, setUsers] = useState([]);

  useEffect(() => {
    async function fetchUsers() {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/users');
        const data = await response.json();
        setUsers(data);
      } catch (error) {
        console.error('Error fetching users:', error);
      }
    }
    fetchUsers();
  }, []); // Empty dependency array means run once

  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}
Output
<ul><li>Leanne Graham</li><li>Ervin Howell</li><li>Clementine Bauch</li>... (list of user names)</ul>
🛡️

Prevention

Always place side effects like API calls inside useEffect with proper dependency arrays to avoid infinite loops. Use async/await for clearer asynchronous code. Handle errors with try/catch to avoid silent failures. Use state hooks to store and render data safely.

Enable linting rules like react-hooks/exhaustive-deps to catch missing dependencies. Test API calls separately to ensure they return expected data.

⚠️

Related Errors

Common issues:

  • Too many re-renders: caused by calling state setters directly in the component body.
  • Cannot read property 'map' of undefined: happens if you try to render data before it is fetched or initialized.
  • Unhandled promise rejection: occurs when fetch errors are not caught.

Fix these by using useEffect, initializing state properly, and adding error handling.

Key Takeaways

Always fetch API data inside useEffect to avoid infinite re-renders.
Use async/await with try/catch inside useEffect for clean and safe API calls.
Store API response in state using useState to trigger component updates.
Add dependency arrays to useEffect to control when the effect runs.
Enable React hooks linting rules to catch common mistakes early.