'Cannot read properties of undefined' in React: fix it at the data layer, not with ?.
The most common JavaScript crash in React apps almost always means one thing: you rendered before the data existed. Loading states, optional chaining, and TypeScript patterns that kill this error for good.
'TypeError: Cannot read properties of undefined (reading 'map')' is the most common crash in React apps, and in a data-fetching component it nearly always has the same shape: the component rendered once BEFORE the fetch resolved, and your JSX assumed the data was already there. The fix isn't sprinkling question marks - it's modeling the loading state honestly.
Why the first render always crashes
const [user, setUser] = useState(); // undefined!
useEffect(() => {
fetchUser(id).then(setUser);
}, [id]);
return <ul>{user.orders.map(...)}</ul>; // 💥 crashes on render #1Effects run AFTER render. So render #1 executes with user === undefined, crashes on user.orders, and the fetch never even gets a chance. This ordering - render first, effect second - is the single fact that explains the whole error class.
Fix 1: an explicit loading gate
if (isLoading) return <Skeleton />;
if (error) return <ErrorState error={error} />;
if (!user) return <NotFound />;
// Below this line, user is guaranteed - TypeScript narrows it too
return <ul>{user.orders.map((o) => <Order key={o.id} {...o} />)}</ul>;Early returns for loading, error, and empty make the happy path unconditional. This is strictly better than optional chaining everywhere, because ?. renders a silently broken half-empty UI, while a gate renders an intentional one. TypeScript rewards you: after the guards, user's type no longer includes undefined.
Fix 2: matching initial state to usage
- Rendering .map immediately? Initialize with useState<Order[]>([]) - an empty list renders as nothing, not a crash.
- Careful with the flip side: [] can't distinguish 'loading' from 'genuinely empty', so pair it with an isLoading flag if the UI needs 'No orders yet'.
- Deep access like data.user.profile.avatar means the API shape and your assumption disagree somewhere - log the actual response before adding guards.
- The 'reading X of undefined' message names the property AFTER the undefined thing: reading 'map' of undefined means the thing you called .map on is undefined - work one step left of X.
Where optional chaining IS the right tool
// Genuinely-optional fields, with a fallback the design intends:
<Avatar src={user.profile?.avatar ?? defaultAvatar} />?. is for data that is legitimately sometimes absent, paired with ?? to say what to show instead. It is not for silencing crashes whose real cause is 'we rendered too early' - that's the loading gate's job. A codebase where every property access has a question mark is a codebase that has stopped tracking which data is actually guaranteed.
This error is a state-modeling problem wearing a crash's clothing. Model loading, error, and empty explicitly, and undefined stops reaching your JSX at all.
Written by Appesto Engineering.