DEV Community

Cover image for Why does my React component crash on the first render when using data fetched inside useEffect()?
CodeSmithNazim
CodeSmithNazim

Posted on

Why does my React component crash on the first render when using data fetched inside useEffect()?

When you fetch data inside useEffect(), the network request is asynchronous and only runs after the initial render has already completed and painted to the screen.

Because React cannot pause rendering to wait for your API response, your component's state is still its initial value (like null or undefined) during that first render. If you try to read properties from your data (like user.name) before the data actually arrives, your app will crash.

⏳ Step-by-Step: The Async Lifecycle

function UserProfile({ userId }) {
  const [user, setUser] = useState(null); // Starts as null

  useEffect(() => {
    fetchUser(userId).then(setUser); // Async API call
  }, [userId]);

  // ⚠️ Safety check is mandatory!
  return (
    <div>
      {user ? <h1>{user.name}</h1> : <div>Loading...</div>}
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Step 1: The Initial Render (The "Loading" State)

  1. React mounts the component.
  2. It initializes the state: user is set to null.
  3. React must return JSX immediately. It cannot halt the browser to wait on a server.
  4. React runs your return statement. Since user is currently null, the ternary operator evaluates the fallback and renders: <div>Loading...</div>.
  5. The browser paints "Loading..." on the screen. The first render is now complete.

Step 2: The useEffect Fires (After Paint)

Now that the first render is safely on the screen, React triggers the useEffect().

It kicks off your asynchronous network call: fetchUser('123').

While the browser is waiting for the server to reply, nothing changes on the screen. It still shows "Loading..."

Step 3: State Updates

Sometime later (e.g., 300ms), the server responds with { name: "Alex" }.

The promise resolves and calls setUser({ name: "Alex" }).

Setting state signals React: "Hey! The data is here. We need to re-render!"

Step 4: The Second Render (The "Data" State)

React re-runs theUserProfile` function.

This time, the user is not null; it is { name: "Alex" }.

The return statement runs again. The ternary operator evaluates to truthy and returns: <h1>Alex</h1>.

The browser updates, replacing "Loading..." with "Alex."

⚠️ Why Conditional Guards Are Mandatory

Because of this exact asynchronous timeline, if you do not write a guard condition, your application will crash during Step 1.

javascript
// ❌ THIS WILL CRASH IMMEDIATELY
return (
<div>
<h1>{user.name}</h1>
</div>
);

During the initial render, user is still null. JavaScript will try to read null.name and throw the classic error:
💡 TypeError: Cannot read properties of null (reading 'name')
Always protect your renders from empty async states using either ternary operators, logical AND (&&), or optional chaining (?.):

`javascript
// Option A: Ternary Operator (Great for loading states)
{user ?

{user.name}

: Loading...}

// Option B: Optional Chaining (Safely returns undefined instead of crashing)

{user?.name}

`

Put your comments; I'll be happy to see feedback if it helps anyone.
Follow me for more.

Top comments (0)