
When Props Object Is Undefined
Fixing the crash caused by props coming through as undefined from the parent component.

Fixing the crash caused by props coming through as undefined from the parent component.
Class is there, but style is missing? Debugging Tailwind CSS like a detective.

Think Android is easier than iOS? Meet Gradle Hell. Learn to fix minSdkVersion conflicts, Multidex limit errors, Namespace issues in Gradle 8.0, and master dependency analysis with `./gradlew dependencies`.

App crashed with TypeError? Learn why 'Null is not a subtype of String' happens and how to make your JSON parsing bulletproof with Zod/Freezed.

Obsessively wrapping everything in `useMemo`? It might be hurting your performance. Learn the hidden costs of memoization and when to actually use it.

I was building a user profile page for my service. The parent component fetches user data from an API, then passes it to a child component.
function ProfilePage() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then(data => setUser(data));
}, []);
return <UserCard user={user} />;
}
function UserCard({ user }) {
return (
<div>
<h1>{user.name}</h1> {/* 🔥 Cannot read property 'name' of null */}
<p>{user.email}</p>
</div>
);
}
Running this code crashes the app. The console shows a red error: "Cannot read property 'name' of null."
At first I thought "I'm clearly fetching data from the API, why is it null?" But the problem was timing. The component renders before the API response arrives.
My misconception was: "When you fetch data in useEffect, the data is already there when the component renders"
But React's rendering order is:
user is null)useEffect executessetUser calleduser has data)The problem is at step 1, we're already trying to read user.name. At this point user is still null, causing an error.
"So what should I do?" I thought. How do you handle when data isn't available?
I understood this problem with this analogy:
"A component is a restaurant kitchen, and props are ingredients. If you start cooking before ingredients arrive, you'll obviously have problems. Wait for ingredients or use substitutes."Ah! That's why you need loading states or optional chaining.
Several solutions exist:
Solution 1: Add loading statefunction ProfilePage() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser()
.then(data => setUser(data))
.finally(() => setLoading(false));
}, []);
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return <UserCard user={user} />;
}
Solution 2: Optional chaining
function UserCard({ user }) {
return (
<div>
<h1>{user?.name ?? 'Unknown'}</h1>
<p>{user?.email ?? 'No email'}</p>
</div>
);
}
Solution 3: Default values
function UserCard({ user = {} }) {
const { name = 'Unknown', email = 'No email' } = user;
return (
<div>
<h1>{name}</h1>
<p>{email}</p>
</div>
);
}
I usually prefer solution 1. It clearly shows users that loading is in progress.
Understanding React component rendering order is crucial:
function Component() {
console.log('1. Render phase');
useEffect(() => {
console.log('3. Effect phase (after render)');
});
return <div>2. JSX returned</div>;
}
Execution order:
useEffect runs after rendering. So on first render, data isn't available yet.
TypeScript catches these problems at compile time:
interface User {
name: string;
email: string;
}
interface UserCardProps {
user: User | null; // Explicitly allow null
}
function UserCard({ user }: UserCardProps) {
if (!user) {
return <div>Loading...</div>;
}
// Now user is definitely User type
return (
<div>
<h1>{user.name}</h1> {/* ✅ Type safe */}
<p>{user.email}</p>
</div>
);
}
TypeScript warns "user might be null, check it."
Real API responses usually have nested objects:
const user = {
profile: {
personal: {
name: 'John',
age: 30
},
contact: {
email: 'john@example.com'
}
}
};
Optional chaining shines here:
// 🔥 Dangerous: crashes if any intermediate is undefined
const name = user.profile.personal.name;
// ✅ Safe: returns undefined if any intermediate is undefined
const name = user?.profile?.personal?.name;
Same for arrays:
function UserList({ users }) {
return (
<ul>
{users.map(user => ( // 🔥 Crashes if users is undefined
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// ✅ Safe version
function UserList({ users = [] }) {
if (users.length === 0) {
return <div>No users</div>;
}
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
From React 18, you can handle this more elegantly with Suspense:
function ProfilePage() {
return (
<Suspense fallback={<div>Loading...</div>}>
<ErrorBoundary fallback={<div>Error!</div>}>
<UserCard />
</ErrorBoundary>
</Suspense>
);
}
function UserCard() {
const user = use(fetchUser()); // React 19 use hook
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
This allows declarative loading and error handling.
I improved my profile page like this:
function ProfilePage() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchUser()
.then(data => setUser(data))
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, []);
if (loading) {
return <LoadingSpinner />;
}
if (error) {
return <ErrorMessage message={error} />;
}
if (!user) {
return <div>User not found</div>;
}
return <UserCard user={user} />;
}
Now handles all cases: loading, error, no data, success.
Since I use this pattern often, I made a custom hook:
function useFetch(fetchFn) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchFn()
.then(setData)
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, []);
return { data, loading, error };
}
// Usage
function ProfilePage() {
const { data: user, loading, error } = useFetch(fetchUser);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
if (!user) return <div>User not found</div>;
return <UserCard user={user} />;
}
Much cleaner!
Had similar issues in forms:
function EditProfile({ initialData }) {
const [formData, setFormData] = useState(initialData);
const handleChange = (e) => {
setFormData({
...formData,
[e.target.name]: e.target.value
});
};
return (
<form>
<input
name="name"
value={formData.name} // 🔥 Crashes if initialData is undefined
onChange={handleChange}
/>
</form>
);
}
Fixed with default values:
function EditProfile({ initialData = {} }) {
const [formData, setFormData] = useState({
name: '',
email: '',
...initialData // Overwrite if exists
});
// Now safe!
}
?. syntax is your best friend.