Fetching Data with fetch()
In this page:
useEffect(() => {
fetch("https://api.example.com/data")
.then(response => {
if (!response.ok) throw new Error("Request failed");
return response.json();
})
.then(data => setData(data));
}, []);
Making a Basic fetch() Request
fetch(url) sends an HTTP request and returns a Promise that resolves to a Response object once the headers arrive. Calling .json() on that response (itself returning another Promise) parses the response body, giving you the actual data.
Example: Making a Basic fetch() Request
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [data, setData] = React.useState(null);
React.useEffect(() => {
setData({ message: "Simulated fetch result" });
}, []);
return <p>{data ? data.message : "Loading..."}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Checking response.ok for HTTP Errors
Since fetch() doesn't reject on HTTP error statuses, checking response.ok (true for 200-299 statuses) is the standard way to detect a failed request and handle it explicitly, usually by throwing an error to be caught separately.
Example: Checking response.ok for HTTP Errors
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [error, setError] = React.useState(null);
React.useEffect(() => {
const ok = false;
if (!ok) setError("Request failed with status 404");
}, []);
return <p style={{color: "red"}}>{error || "Success"}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Fetching Inside useEffect, Not the Render Body
Calling fetch() directly in a component's body (outside useEffect) runs it on every single render, including the re-render caused by the fetch itself, creating an infinite request loop.
Wrapping it in useEffect with an empty dependency array runs it exactly once, after the initial render.
Example: Fetching Inside useEffect, Not the Render Body
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
function App() {
const [data, setData] = React.useState(null);
React.useEffect(() => {
// fetch("/api/data").then(r => r.json()).then(setData);
setData("Loaded once, safely, via useEffect");
}, []);
return <p>{data || "Loading..."}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Forgetting fetch() only rejects on network failure, not on HTTP error statuses like 404/500 — you must check response.ok yourself.
- Not calling .json() (or another body-reading method) to actually parse the response body.
- Running fetch() directly in the component body instead of inside useEffect, causing an infinite loop of requests.
- fetch() is the built-in browser API for making HTTP requests, returning a Promise.
- fetch() only rejects on network errors — checking response.ok is required to catch HTTP error statuses.
- response.json() (also a Promise) parses the response body as JSON.
- Data fetching in a component belongs inside useEffect, not directly in the render body.
fetch() is supported in all modern browsers (not IE11 without a polyfill); works with any React version.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: