I've reviewed a lot of React codebases.
The same mistakes appear every time.
Here are the 6 that waste the most time — and exactly how to fix them.
1. useEffect With Missing Dependencies
// 🚫 Bug — userId changes but effect never re-runs
useEffect(() => {
fetchUserData(userId);
}, []); // missing userId
// ✅ Correct
useEffect(() => {
fetchUserData(userId);
}, [userId]); // re-runs when userId changes
React's ESLint plugin catches this — install eslint-plugin-react-hooks and enable it. Never ignore the dependency array warning.
2. Creating Objects and Arrays Inside JSX
// 🚫 New object created on every render
// This causes child component to re-render every time
function Parent() {
return (
<Child
style={{ color: 'red' }} // new object every render
items={['a', 'b', 'c']} // new array every render
/>
);
}
// ✅ Move constants outside component
const CHILD_STYLE = { color: 'red' };
const ITEMS = ['a', 'b', 'c'];
function Parent() {
return <Child style={CHILD_STYLE} items={ITEMS} />;
}
If the value never changes — move it outside the component. If it depends on props or state — use useMemo.
3. Fetching Data in useEffect
// 🚫 Client-side fetching when you don't need to
'use client';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users')
.then(r => r.json())
.then(setUsers);
}, []);
return <div>{users.map(u => <div key={u.id}>{u.name}</div>)}</div>;
}
// ✅ Fetch on server — faster, no loading flash, better SEO
async function UserList() {
const users = await fetch('/api/users').then(r => r.json());
return <div>{users.map(u => <div key={u.id}>{u.name}</div>)}</div>;
}
In Next.js — if data doesn't depend on user interaction, fetch it in a server component. Instant render, no loading state needed.
4. Not Using Key Prop Correctly
// 🚫 Using index as key — causes bugs with reordering
{items.map((item, index) => (
<Item key={index} data={item} />
))}
// 🚫 No key at all
{items.map((item) => (
<Item data={item} />
))}
// ✅ Use stable unique ID
{items.map((item) => (
<Item key={item.id} data={item} />
))}
Using index as key causes React to reuse the wrong DOM elements when items are added, removed, or reordered. Always use a stable unique ID.
5. Unnecessary useState for Derived Values
// 🚫 Keeping derived state in useState
function Cart({ items }) {
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(items.reduce((sum, item) => sum + item.price, 0));
}, [items]);
return <div>Total: ${total}</div>;
}
// ✅ Calculate directly — no state needed
function Cart({ items }) {
const total = items.reduce((sum, item) => sum + item.price, 0);
return <div>Total: ${total}</div>;
}
// ✅ If expensive calculation — use useMemo
function Cart({ items }) {
const total = useMemo(
() => items.reduce((sum, item) => sum + item.price, 0),
[items]
);
return <div>Total: ${total}</div>;
}
If you can calculate a value from existing props or state — don't put it in useState. It creates an extra render cycle and makes your code harder to follow.
6. Giant Components That Do Everything
// 🚫 One component doing too much
function Dashboard() {
const [users, setUsers] = useState([]);
const [stats, setStats] = useState({});
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [selectedUser, setSelectedUser] = useState(null);
const [modalOpen, setModalOpen] = useState(false);
// ...50 more lines of state
return (
// ...200 lines of JSX
);
}
// ✅ Split by responsibility
function Dashboard() {
return (
<div>
<StatsOverview />
<UserTable />
<RecentActivity />
</div>
);
}
// Each component manages its own state and data
function UserTable() {
const [selectedUser, setSelectedUser] = useState(null);
// only user table logic here
}
Rule of thumb: if a component has more than 3 useState calls or more than 100 lines — it probably needs to be split.
Summary
| Mistake | Fix |
|---|---|
| Missing useEffect deps | Add all dependencies, use ESLint plugin |
| Objects/arrays in JSX | Move outside component or use useMemo |
| Fetching in useEffect | Use server components in Next.js |
| Index as key | Always use stable unique IDs |
| Derived state in useState | Calculate directly or use useMemo |
| Giant components | Split by single responsibility |
These are the most common bugs I see in React codebases. Fix them and your code will be faster and easier to maintain.
I apply all these patterns in my Next.js templates:
Get the templates: https://pixelanas.gumroad.com
What React mistake cost you the most time? Drop it below 👇
Anas — full-stack Next.js developer building SaaS products and premium templates. X: @pixelanas