Forgetting to Initialize useState
with a Default Value
A common React mistake is declaring state with useState
but forgetting to provide an initial value, which can lead to undefined
errors and unexpected behavior.
The Problem
// ❌ Wrong - No initial value provided
const [count, setCount] = useState(); // count is undefined
// Later in component...
return <div>{count + 1}</div>; // NaN error
Why this is problematic:
- State variable starts as
undefined
- Operations on undefined values cause errors (
undefined + 1 = NaN
) - Can lead to runtime crashes
- Makes component behavior unpredictable
Correct Solutions
1. Basic Initialization
// ✅ Correct - Primitive value
const [count, setCount] = useState(0); // number
const [name, setName] = useState(''); // string
const [isActive, setIsActive] = useState(false); // boolean
2. Object Initialization
// ✅ Correct - Object state
const [user, setUser] = useState({
name: '',
email: '',
age: null
});
3. Array Initialization
// ✅ Correct - Array state
const [items, setItems] = useState([]); // Empty array
4. Lazy Initialization (for expensive computations)
// ✅ Correct - Function initializer
const [data, setData] = useState(() => {
const initialData = calculateExpensiveValue();
return initialData;
});
Common Use Cases
Form Handling
function Form() {
const [formData, setFormData] = useState({
username: '',
password: '',
remember: false
});
// ...form handling logic
}
API Data Loading
function UserProfile() {
const [user, setUser] = useState(null); // Explicit null for loading state
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
fetchUser().then(data => {
setUser(data);
setIsLoading(false);
});
}, []);
if (isLoading) return <Spinner />;
return <Profile user={user} />;
}
Best Practices
- Always initialize state – Even if just with
null
- Match the expected type – Initialize with proper data structure
- Consider null states – For async data loading
- Document expected shape – With PropTypes or TypeScript
- Use descriptive defaults – That reveal the expected data structure
Common Mistakes to Avoid
- Partial initialization:
const [user, setUser] = useState({}); // ❌ Missing properties
console.log(user.name.first); // Error if nested
- Incorrect type defaults:
const [count, setCount] = useState(''); // ❌ Expecting number
setCount(count + 1); // String concatenation
- Forgetting async states:
const [data, setData] = useState(); // ❌ No loading state
return <div>{data.items.length}</div>; // Error when loading
Advanced Patterns
TypeScript Integration
interface User {
id: number;
name: string;
email?: string;
}
function UserComponent() {
const [user, setUser] = useState<User | null>(null);
// ...type-safe operations
}
Reducer Alternative
// For complex state shapes
const [state, dispatch] = useReducer(reducer, {
items: [],
loading: false,
error: null
});
- What’s the expected data type?
- What’s a sensible default value?
- How should the component behave before data is loaded?