6 React Mistakes That Waste Hours — And How to Fix Them

typescript dev.to

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
Enter fullscreen mode Exit fullscreen mode

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} />;
}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

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} />
))}
Enter fullscreen mode Exit fullscreen mode

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>;
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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

Source: dev.to

arrow_back Back to Tutorials