React ยท Chapter 31 of 42
Loading and Error States
Good UX requires showing users what's happening: a loading indicator while data is fetched, and a clear error message if something goes wrong.
Manage these as explicit pieces of state alongside your data, rather than guessing from the data's presence.
Tracking states explicitly
Use separate state for `loading`, `error`, and `data` so your UI can render distinct views for each case.
Rendering the right UI
Check `loading` first, then `error`, then render the actual data โ this ordering keeps logic predictable.
Example 1 (jsx)
function Profile({ userId }) {
const [state, setState] = useState({ loading: true, error: null, data: null });
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setState({ loading: false, error: null, data }))
.catch(error => setState({ loading: false, error, data: null }));
}, [userId]);
if (state.loading) return <p>Loading...</p>;
if (state.error) return <p>Error: {state.error.message}</p>;
return <p>{state.data.name}</p>;
}Output
Loading... then either an error message or the user's nameThe component checks loading, then error, then renders data.
Key points
- Track loading, error, and data as explicit state.
- Check loading first, then error, then render data.
- Clear feedback improves perceived performance and trust.
- Avoid inferring loading/error purely from data being null.
๐ก Note: Skeleton loaders and spinners both work well; choose based on your design system.
