Loading and Error States
In this page:
const [status, setStatus] = useState("loading"); // loading | error | success
if (status === "loading") return <p>Loading...</p>;
if (status === "error") return <button onClick={retry}>Try again</button>;
return <DataView data={data} />;
The Three States of a Data Request
Any data-fetching UI generally needs to represent three distinct situations: still waiting for the response (loading), the request failed (error), and the response arrived successfully (success, with actual data to show).
Designing for all three upfront avoids a UI that looks broken during real-world network conditions.
Example: The Three States of a Data 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 [status] = React.useState("loading");
if (status === "loading") return <p>Loading...</p>;
if (status === "error") return <p>Something went wrong.</p>;
return <p>Data loaded successfully!</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Using One Status String Instead of Multiple Booleans
Tracking isLoading and isError as two separate booleans allows nonsensical combinations, like both being true at once. A single status field with distinct string values (idle, loading, error, success) can only ever represent one state at a time, which is a more accurate model of reality.
Example: Using One Status String Instead of Multiple Booleans
<!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 [status, setStatus] = React.useState("idle");
const startLoading = () => setStatus("loading");
return (
<div>
<button onClick={startLoading}>Load Data</button>
<p>Status: {status}</p>
</div>
);
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
Giving the Error State a Retry Option
A good error state doesn't just say 'something went wrong' — it offers a way to try again, like a Retry button that re-runs the original fetch logic and resets the status back to loading.
Example: Giving the Error State a Retry Option
<!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 [status, setStatus] = React.useState("error");
const retry = () => setStatus("loading");
if (status === "error") return <div><p>Failed to load.</p><button onClick={retry}>Retry</button></div>;
return <p>Status: {status}</p>;
}
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
</script>
</body>
</html>
- Only handling the success case and forgetting to design what the UI looks like while loading or after an error.
- Using a single boolean for three possible states, making 'loading AND error at once' representable when it shouldn't be.
- Not resetting the error state when a new request starts, leaving a stale error visible during a retry.
- A data-fetching component typically has three states: loading, error, and success (with data).
- Using a single status string (idle/loading/error/success) avoids invalid state combinations that separate booleans allow.
- Each state should have its own distinct UI: a spinner/message, an error message with retry option, and the actual content.
- Resetting error/loading state at the start of a new request keeps the UI accurate during retries.
No React-version restriction — built from useState and conditional rendering.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: