Waiting for async function in React component Showing Spinner
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
In React, async operations like API calls do not block rendering. To show a loading spinner while waiting for data, you use a state variable (typically loading) that starts as true, triggers the spinner, and is set to false when the async operation completes. The standard pattern is to call the async function inside useEffect, update state with useState, and conditionally render a spinner or the data based on the loading state. This pattern applies to any async operation — API fetches, file reads, or computations.
Basic Loading Spinner Pattern
The loading state controls which UI is rendered. The finally block ensures setLoading(false) runs whether the fetch succeeds or fails.
Spinner Component
Custom Hook for Loading State
The custom hook encapsulates the loading/error/data pattern and includes a cleanup flag to prevent state updates on unmounted components.
Multiple Async Operations
Promise.all fetches all data in parallel. The spinner shows until every request completes.
Skeleton Loading Pattern
Skeleton screens show placeholder shapes that match the final layout, giving users a sense of structure before data loads. This feels faster than a generic spinner.
Common Pitfalls
- Defining async functions directly in useEffect:
useEffect(async () => { ... })returns a promise, but React expectsuseEffectto return a cleanup function or nothing. Define the async function inside and call it:useEffect(() => { async function load() { ... } load(); }, []). - Not handling component unmount: If the component unmounts before the fetch completes, calling
setStatetriggers a warning. Use acancelledflag in the cleanup function:return () => { cancelled = true; }and check it before callingsetState. - Forgetting the loading state on re-fetch: When dependencies change (e.g.,
userId), the component re-fetches data. If you do not resetloadingtotrueat the start, stale data shows briefly before the new data loads. AlwayssetLoading(true)before starting a new fetch. - Not showing errors alongside loading: If a fetch fails,
loadingbecomesfalsebutdataremainsnull. Without anerrorstate, the component renders with no data and no explanation. Always track errors alongside loading state. - Blocking render with synchronous operations: Heavy computations in the render path freeze the UI even with a spinner. Move expensive work into
useEffector a Web Worker so the spinner can actually animate while the work happens in the background.
Summary
- Use
useStateforloading,data, anderrorto track async operation state - Call async functions inside
useEffect, not directly as the effect callback - Render a spinner or skeleton when
loadingistrue, the actual content whenfalse - Use
Promise.allfor parallel fetches with a single loading indicator - Clean up with a
cancelledflag to prevent state updates on unmounted components
Related reading
- Waiting for async function to return true or false - how do I check return value?
- Waiting for asynchronous callback in Android's IntentService
- Waiting on an IAsyncResult method that waits on another IAsyncResult Chaining
- Waiting on multiple threads to complete in Java
- Waiting for leadership elections in KafkaJS
- Waiting until two async blocks are executed before starting another block
- Waiting on the cancellation of an asynchronous workflow
- Waiting until the task finishes
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.