DEV Community

Cover image for Mastering React Performance: A Developer's Guide to Preventing Unnecessary Re-renders
Omkar Karale
Omkar Karale

Posted on โ€ข Edited on

8 1

Mastering React Performance: A Developer's Guide to Preventing Unnecessary Re-renders

React's virtual DOM and component-based architecture make it incredibly efficient, but without proper optimization, your application can still suffer from performance issues. Let's dive into proven strategies to eliminate unnecessary re-renders and boost your React app's performance.

Understanding Re-renders: The Foundation

Before we optimize, we need to understand when and why React components re-render:

  • When a component's state changes
  • When its props change
  • When its parent component re-renders

Not all re-renders are bad, but unnecessary ones can impact performance.
While these scenarios might seem overlapping, they each represent distinct cases that require different optimization approaches.
Let's explore how to prevent them.

1. React.memo: Your First Line of Defense

const MovieCard = React.memo(({ title, rating, onLike }) => {
  console.log(`MovieCard rendered: ${title}`);
  return (
    <div className="card">
      <h3>{title}</h3>
      <span>Rating: {rating}/10</span>
      <button onClick={onLike}>Like</button>
    </div>
  );
});

// Usage
<MovieCard 
  title="Inception" 
  rating={9.3} 
  onLike={() => handleLike('inception')} 
/>
Enter fullscreen mode Exit fullscreen mode

๐Ÿ’ก Pro Tip: While React.memo is powerful, use it strategically. Memoizing everything can actually hurt performance.

2. useCallback: Stabilizing Function References

const MovieList = () => {
  const [movies, setMovies] = useState([]);

  const handleLike = useCallback((movieId) => {
    setMovies(prevMovies => 
      prevMovies.map(movie => 
        movie.id === movieId 
          ? { ...movie, likes: movie.likes + 1 }
          : movie
      )
    );
  }, []); // Empty deps array as it only uses setState

  return movies.map(movie => (
    <MovieCard
      key={movie.id}
      {...movie}
      onLike={() => handleLike(movie.id)}
    />
  ));
};
Enter fullscreen mode Exit fullscreen mode

3. useMemo: Caching Complex Calculations

const MovieAnalytics = ({ movies }) => {
  const statistics = useMemo(() => ({
    averageRating: movies.reduce((acc, m) => acc + m.rating, 0) / movies.length,
    topRated: [...movies].sort((a, b) => b.rating - a.rating)[0],
    totalLikes: movies.reduce((acc, m) => acc + m.likes, 0)
  }), [movies]);

  return (
    <div>
      <h2>Analytics Dashboard</h2>
      <p>Average Rating: {statistics.averageRating.toFixed(1)}</p>
      <p>Most Popular: {statistics.topRated.title}</p>
      <p>Total Likes: {statistics.totalLikes}</p>
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

4. State Management Best Practices

Lifting State Up (When Needed)

const MovieApp = () => {
  const [favorites, setFavorites] = useState(new Set());

  // Lifted state handler
  const toggleFavorite = useCallback((movieId) => {
    setFavorites(prev => {
      const next = new Set(prev);
      if (next.has(movieId)) next.delete(movieId);
      else next.add(movieId);
      return next;
    });
  }, []);

  return (
    <div>
      <MovieList onFavorite={toggleFavorite} />
      <FavoritesList ids={favorites} />
    </div>
  );
};
Enter fullscreen mode Exit fullscreen mode

5. Advanced Optimization Techniques

Custom Hooks for Complex Logic

function useMovieData(movieId) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    let mounted = true;

    async function fetchMovie() {
      setLoading(true);
      try {
        const response = await fetch(`/api/movies/${movieId}`);
        const movie = await response.json();
        if (mounted) {
          setData(movie);
          setLoading(false);
        }
      } catch (error) {
        if (mounted) {
          console.error('Failed to fetch movie:', error);
          setLoading(false);
        }
      }
    }

    fetchMovie();
    return () => {
      mounted = false;
    };
  }, [movieId]);

  return { data, loading };
}
Enter fullscreen mode Exit fullscreen mode

6. Profiling and Debugging

Using React Developer Tools

  • Enable React Developer Tools Profiler
  • Record component renders
  • Identify unnecessary re-renders
  • Measure render durations

Performance Checklist

โœ… Use React.memo for pure functional components
โœ… Implement useCallback for event handlers passed as props
โœ… Apply useMemo for expensive calculations
โœ… Avoid inline object creation in renders
โœ… Utilize proper key props in lists
โœ… Profile your app regularly

Common Pitfalls to Avoid

โŒ Over-optimization
โŒ Premature optimization
โŒ Memoizing everything
โŒ Creating new objects/arrays in render
โŒ Deep component nesting

Looking Ahead: React 19 and Performance

React 19 brings automatic performance improvements:

  • Enhanced automatic batching
  • Improved concurrent rendering
  • Better scheduling of state updates

Conclusion

Performance optimization in React is a balance between code complexity and actual performance gains. Start with the basics, measure your app's performance, and optimize where needed. Remember: premature optimization is the root of all evil!

โœจ I hope you found this helpful! Donโ€™t forget to like and follow me for more React tips and tricks!

๐Ÿš€ Follow me on X (Twitter) and LinkedIn for daily web development tips and insights!

๐Ÿ’ป Keep coding, keep creating, and keep improving!

Wishing you all success and positivity on this wonderful day. Letโ€™s make it amazing together! ๐ŸŒŸ

react #javascript #webdev #performance

Top comments (8)

Collapse
 
himanshu_code profile image
Himanshu Sorathiya โ€ข

Just a curious question ( currently haven't read whole article, directly asking a question of ), in The Foundation you listed 3 scenarios when component re renders,

  • When a component's state changes
  • When its props change
  • When its parent component re-renders

Don't you think all of them are redundant, cause when does components props changes, because of its Parents state changed, and state changes occurs re render meaning all of its child will also be re rendered

Am I correct or missing a piece of puzzle?

Collapse
 
onkar_karale profile image
Omkar Karale โ€ข โ€ข Edited

Thanks for the excellent question! While these scenarios might seem redundant at first glance, they're actually distinct cases that are worth understanding separately. Let me explain why:

Props changes without parent re-render:
Props can change without a parent component's state changing. For example:

function Parent() {
  return <Child prop={someExternalStore.value} />; // Props can change due to external updates
}
Enter fullscreen mode Exit fullscreen mode

State changes in isolation:
A component can update its own state without any parent involvement:

function Counter() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
Enter fullscreen mode Exit fullscreen mode

Parent re-renders without prop changes:
A parent can re-render without changing the props passed to its children:

function Parent() {
  const [unrelatedState, setUnrelatedState] = useState(0);
  return (
    <>
      <button onClick={() => setUnrelatedState(prev => prev + 1)}>
        Update Parent
      </button>
      <Child staticProp="hello" /> {/* Props don't change but Child still re-renders */}
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

This is actually why we have optimization tools like React.memo() - to prevent re-renders when a parent re-renders but the child's props haven't changed.

Your observation about the relationship between these scenarios is insightful - they are indeed related, but understanding them separately helps us:

  1. Better diagnose performance issues
  2. Choose the right optimization strategy
  3. Understand when to use tools like React.memo, useMemo, and useCallback

The post lists all three scenarios because each might require different optimization approaches, even though they can sometimes overlap.

Collapse
 
himanshu_code profile image
Himanshu Sorathiya โ€ข

Helpful. Thanks for this.
Summarization: ( Help if I'm again wrong๐Ÿ˜…๐Ÿ™ƒ)
Component will re renders when

  • It's states changes ( useState, useReducer )
  • when it's subscribed context changes ( ContextAPI, Redux store )
  • when it's Parent's re renders ( By state change or it's subscribed context change ), and it's not necessary that it's children uses Parent's values as props or not, they will be re rendered no matter what, but using Memoization, unnecessary renders can be prevented

Am I right?

I may be wrong, currently I'm learning React and just completed Redux, so pleases correct me if I make mistake

Thread Thread
 
onkar_karale profile image
Omkar Karale โ€ข โ€ข Edited

You're mostly correct!

A component re-renders when:

  • Its state changes (e.g., useState, useReducer updates).
  • Its subscribed context changes (e.g., Context API or Redux store updates).
  • Its parent re-renders, regardless of whether the child uses the parent's values or not.

To optimize rendering:

  • Use React.memo to prevent unnecessary renders when props don't change.
  • Use useCallback and useMemo to memoize functions and computations passed to children.

Props Changes:

  • A component will re-render if it receives new props, even if its internal state hasn't changed
  • This is separate from parent re-renders (though often related)

Hooks Changes:

  • Changes to values returned by hooks like useMemo, useCallback, useEffect can trigger re-renders
  • useRef changes do NOT trigger re-renders (this is an important distinction)

Summary:

  • React.memo() for functional components
  • useCallback for functions
  • useMemo for expensive computations
  • Using useRef for values that shouldn't trigger re-renders

You're doing greatโ€”keep learning and experimenting with React and Redux! ๐ŸŽฏ๐Ÿš€

Thread Thread
 
himanshu_code profile image
Himanshu Sorathiya โ€ข

Thanks for this.

Thread Thread
 
onkar_karale profile image
Omkar Karale โ€ข

๐Ÿคโœ…

Collapse
 
2priolinkedin profile image
2PR Enhance LinkedIn Profile โ€ข

Nice!

Collapse
 
onkar_karale profile image
Omkar Karale โ€ข

๐Ÿ‘๐Ÿ‘๐Ÿค

The best way to debug slow web pages cover image

The best way to debug slow web pages

Tools like Page Speed Insights and Google Lighthouse are great for providing advice for front end performance issues. But what these tools canโ€™t do, is evaluate performance across your entire stack of distributed services and applications.

Watch video