How to Fetch Data from an API and Display It as Raw Data in React
Use the useState hook to store API responses and useEffect to trigger fetch requests after component mount, then render the raw JSON using JSON.stringify inside a <pre> tag.
Fetching data from an API and displaying it as raw data in React relies on the library's declarative state management system. In the facebook/react repository, the core implementations of the hooks that enable this pattern are located in packages/react/src/ReactHooks.js. By combining useState for state storage and useEffect for side effects, you create a non-blocking data-fetching flow that displays raw API responses without interfering with the initial render.
Core Hooks for API Data Fetching
React's function components manage data through hooks. Two specific hooks defined in the React source code handle the complete lifecycle of fetching and displaying external data.
Managing State with useState
The useState hook provides reactive state that persists across renders and triggers UI updates when modified. According to the facebook/react source code, the implementation in packages/react/src/ReactHooks.js (lines 66-71) defines how React maintains state consistency. When fetching API data, initialize state as null or an empty object, then update it via the setter function once the network promise resolves.
Handling Side Effects with useEffect
The useEffect hook executes operations that touch external systems—such as network requests—after React commits changes to the DOM. The implementation in packages/react/src/ReactHooks.js (lines 87-100) ensures effects run after the paint phase, preventing fetch operations from blocking the initial render. Passing an empty dependency array [] ensures the effect executes only once when the component mounts, making it ideal for one-time API calls.
Fetching and Displaying Raw API Data
To fetch data from an API and display it as raw data in React, combine the hooks with the browser's native Fetch API. The following component demonstrates the complete pattern, including loading states and error handling:
import React, { useState, useEffect } from 'react';
export default function ApiViewer() {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('https://api.example.com/items')
.then(res => {
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return res.json();
})
.then(json => {
setData(json);
setLoading(false);
})
.catch(err => {
setError(err.message);
setLoading(false);
});
}, []);
if (loading) return <p>Loading…</p>;
if (error) return <p>Error: {error}</p>;
return (
<pre>{JSON.stringify(data, null, 2)}</pre>
);
}
This example encapsulates the fetch call inside useEffect to prevent blocking the initial render, uses useState to store the response, and displays the raw JSON using JSON.stringify with indentation for readability inside a <pre> tag.
Dynamic Data Fetching with Dependencies
When you need to refetch data based on changing props or state, add those values to the useEffect dependency array. This pattern is essential when fetching data from an API and displaying it as raw data in React based on user interaction:
export function UserInfo({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
setUser(null);
fetch(`https://api.example.com/users/${userId}`)
.then(r => r.json())
.then(setUser);
}, [userId]);
return user ? <pre>{JSON.stringify(user, null, 2)}</pre> : <p>Loading…</p>;
}
Adding userId to the dependency array ensures React runs the effect whenever the prop updates, keeping the displayed raw data synchronized with the current selection.
Modern Async/Await Syntax
While promise chaining works correctly, many developers prefer async/await for readability. You can use this syntax inside useEffect by defining an inner async function:
useEffect(() => {
async function load() {
try {
const res = await fetch('https://api.example.com/items');
const json = await res.json();
setData(json);
} catch (e) {
setError(e.message);
} finally {
setLoading(false);
}
}
load();
}, []);
Both promise chaining and async/await are fully supported in React's effect system. The choice depends on your team's coding standards and error-handling preferences.
Summary
- Use
useStateto hold API data, loading states, and errors, triggering re-renders when values change according to the implementation inpackages/react/src/ReactHooks.js. - Use
useEffectwith an empty dependency array to execute fetch calls after the initial paint, preventing render blocking as defined inpackages/react/src/ReactHooks.js(lines 87-100). - Display raw data by rendering
JSON.stringify(data, null, 2)inside a<pre>tag to preserve formatting and indentation. - Handle dynamic fetching by adding dependencies to the effect array, causing React to refetch when props or state change.
- Choose your syntax between promise chaining and async/await based on readability needs; both work identically within the React hook lifecycle.
Frequently Asked Questions
How do I prevent my React component from rendering before data loads?
Initialize your state to null or a loading indicator, then conditionally render content based on that state. Return a loading message when the data is null, and render the actual content—including your raw JSON display—only after the fetch promise resolves and updates state via the setter function.
Why must I use useEffect instead of calling fetch directly in the component body?
Calling fetch directly in the component body would execute during every render, creating infinite loops and blocking the main thread. The useEffect hook—specifically implemented in packages/react/src/ReactHooks.js—guarantees that side effects run after React commits the DOM, ensuring the initial paint completes before network requests begin.
Can I display raw API data without using JSON.stringify?
While you can attempt to render objects directly in JSX, React will throw an error because JSX cannot render objects as children. You must convert the data to a string first. JSON.stringify(data, null, 2) is the standard approach for displaying raw JSON because it formats the output with indentation, making it readable when wrapped in a <pre> tag.
How do I handle API errors gracefully in React?
Track error state using a separate useState variable initialized to null. In your useEffect fetch logic, use a .catch() block (or try/catch with async/await) to capture errors and update the error state. Conditionally render an error message in your JSX when the error state exists, providing clear feedback without crashing the application.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →