
ReactJS
Follow these patterns to write state code that's easy to understand, maintain, and debug.
State should be as simple as possible. Only store what the component truly needs, and derive everything else from that.
Don't store calculated values in state. If you can compute something from other state, calculate it when you need it. This prevents bugs from inconsistent state.
import { useState } from 'react';
// ❌ BAD: Storing both items and count
function BadTodoList() {
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
const addItem = (item) => {
setItems([...items, item]);
setCount(count + 1); // Redundant!
};
}
// ✅ GOOD: Only store items, calculate count
function GoodTodoList() {
const [items, setItems] = useState([]);
const addItem = (item) => {
setItems([...items, item]);
};
const count = items.length; // Derived, not stored
return <p>Items: {count}</p>;
}<details>
<summary>📚 More Examples</summary>
// Example: Derived values
function Calculator() {
const [quantity, setQuantity] = useState(1);
const [pricePerItem, setPricePerItem] = useState(10);
// ✅ Calculate total when needed
const total = quantity * pricePerItem;
return (
<div>
<p>Total: ${total}</p>
<input
type="number"
value={quantity}
onChange={(e) => setQuantity(parseInt(e.target.value))}
/>
</div>
);
}</details>
Always give state a meaningful initial value. Avoid `undefined` or `null` if possible.
import { useState } from 'react';
// ❌ AVOID: Undefined initial state
function BadComponent() {
const [data, setData] = useState(); // undefined
}
// ✅ GOOD: Meaningful initial values
function GoodComponent() {
const [items, setItems] = useState([]); // Empty array
const [count, setCount] = useState(0); // 0
const [isLoading, setIsLoading] = useState(false); // false
const [text, setText] = useState(''); // Empty string
}<details>
<summary>📚 More Examples</summary>
// Example: Initializing different types
function Form() {
const [formData, setFormData] = useState({
email: '',
password: '',
remember: false,
});
const [errors, setErrors] = useState(null);
return (
<div>
{errors && <p style={{ color: 'red' }}>{errors}</p>}
</div>
);
}</details>
Update state for one reason at a time. Avoid updating multiple related values together unless they must always stay in sync.
import { useState } from 'react';
function SearchPage() {
const [searchTerm, setSearchTerm] = useState('');
const [isSearching, setIsSearching] = useState(false);
const [results, setResults] = useState([]);
const handleSearch = async (term) => {
setSearchTerm(term);
if (term.length > 0) {
setIsSearching(true);
const data = await fetch(`/api/search?q=${term}`);
setResults(data);
setIsSearching(false);
} else {
setResults([]);
}
};
return (
<div>
<input
value={searchTerm}
onChange={(e) => handleSearch(e.target.value)}
placeholder="Search"
/>
{isSearching && <p>Searching...</p>}
{results.map((r) => <div key={r.id}>{r.name}</div>)}
</div>
);
}| Pattern | Reason |
|---|---|
| Keep it simple | Less state = fewer bugs |
| Derive computed values | Single source of truth |
| Meaningful initial values | Avoid undefined issues |
| One change per update | Clearer state flow |
Ready to practice? Challenges | Next: State with Forms
Resources
Ojasa Mirai
Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.
Learn Deep • Build Real • Verify Skills • Launch Forward