React is fast, but your code might be slowing it down! Many developers unknowingly make performance mistakes that lead to unnecessary re-renders and sluggish UI updates.

However, blindly applying useMemo, useCallback, and useRef everywhere won't magically optimize your app. In fact, improper usage can make your code harder to maintain without significant performance benefits.

Let's dive into common performance pitfalls in React and how to fix them the right way.

Not a Member? Read for FREE here.

❌ 1. Overusing useMemo and useCallback Without Measuring Impact

🚨 The Mistake: Many developers assume that wrapping every function in useCallback and every value in useMemo will automatically improve performance. But the truth is, these hooks should be used selectively.

Example (BAD)

const ExpensiveComponent = React.memo(({ compute, value }) => {
  return <div>Computed Value: {compute(value)}</div>;
});

const ParentComponent = ({ number }) => {
  const compute = useCallback((num) => num * 2, []); // ❌ Unnecessary useCallback
  return <ExpensiveComponent compute={compute} value={number} />;
};

🚨 Problem: The function is simple, so memoizing it with useCallback has no measurable benefit.

Correct Approach (Use useCallback only if necessary)

const ExpensiveComponent = React.memo(({ compute, value }) => {
  return <div>Computed Value: {compute(value)}</div>;
});

const ParentComponent = ({ number }) => {
  const compute = (num) => num * 2; // ✅ Simpler and clearer
  return <ExpensiveComponent compute={compute} value={number} />;
};

🛠 When Should You Use useCallback and useMemo?

  • When passing functions to memoized components (React.memo)
  • When dealing with expensive calculations
  • When dependencies change frequently in useEffect/useCallback

Example Where useMemo is Actually Helpful:

const sortedData = useMemo(() => heavySortingFunction(data), [data]);

👉 Fix: Don't blindly use useMemo or useCallback—use them only if performance profiling shows a benefit.

❌ 2. Using useRef Instead of useState for Values That Affect UI

🚨 The Mistake: Some developers replace useState with useRef thinking it will improve performance.

Example (BAD)

const Counter = () => {
  const count = useRef(0); // ❌ Won't trigger re-renders

  return (
    <div>
      <p>Count: {count.current}</p> 
      <button onClick={() => count.current += 1}>Increment</button>
    </div>
  );
};

🚨 Problem: useRef does not trigger UI updates, so the counter won't reflect changes!

Correct Approach: Use useState for UI updates

const Counter = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p> {/* ✅ UI updates properly */}
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

🛠 When Should You Use useRef?

✅ When storing values that should persist across renders but don't need to trigger a re-render. ✅ When referencing DOM elements (useRef is perfect for handling focus, scroll position, etc.).

Example Where useRef is Useful:

const inputRef = useRef(null);

const focusInput = () => {
  inputRef.current.focus(); // ✅ Works without re-rendering
};

return <input ref={inputRef} />;

👉 Fix: Use useState when the value affects rendering, and useRef for non-reactive values.

❌ 3. Ignoring React's Built-in Performance Optimizations

🚨 The Mistake: Many developers forget about React.memo, lazy loading, and Suspense, leading to unnecessary rendering.

Optimize Expensive Components with React.memo

const ExpensiveComponent = React.memo(({ data }) => {
  console.log("Re-rendered!");
  return <div>{data}</div>;
});

👉 Now, ExpensiveComponent only re-renders when data changes.

Lazy Load Heavy Components for Faster Initial Load

const HeavyComponent = React.lazy(() => import("./HeavyComponent"));

return (
  <Suspense fallback={<p>Loading...</p>}>
    <HeavyComponent />
  </Suspense>
);

👉 This improves performance by only loading components when needed.

🚀 Final Thoughts: Measure Before Optimizing!

Many React performance optimizations should be used selectively, not blindly applied everywhere.

✅ Golden Rules for Performance Optimization in React:

Don't overuse useMemo and useCallback – measure performance impact first! ✔ Use useRef only when necessary – don't replace useState for reactive values. ✔ Optimize rendering with React.memo and lazy loading when necessary. ✔ Use React Developer Tools to profile performance before optimizing.

💡 What's your biggest React performance lesson? Let's discuss in the comments! 🚀

Key Improvements Based on Feedback:

  1. Clarified when useMemo and useCallback are beneficial (not just always use them).
  2. Explained when useRef is useful and when it shouldn't replace useState.
  3. Added better examples to illustrate correct usage.
  4. Encouraged performance measurement instead of premature optimization.

Connect with Me

If you enjoyed this post and would like to stay updated with more content like this, feel free to connect with me on social media:

Email: Email me on dipaksahirav@gmail.com for any questions, collaborations, or just to say hi!

I appreciate your support and look forward to connecting with you!