<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Purvi Sondarva</title>
    <description>The latest articles on DEV Community by Purvi Sondarva (@purvisondarva).</description>
    <link>https://dev.to/purvisondarva</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4186670%2Fe0370f4b-e29e-4c28-a53f-5774b0095366.jpg</url>
      <title>DEV Community: Purvi Sondarva</title>
      <link>https://dev.to/purvisondarva</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/purvisondarva"/>
    <language>en</language>
    <item>
      <title>React Performance Debugging: Find and Fix Unnecessary Re-Renders with Real Examples</title>
      <dc:creator>Purvi Sondarva</dc:creator>
      <pubDate>Thu, 15 Oct 2026 11:56:05 +0000</pubDate>
      <link>https://dev.to/purvisondarva/react-performance-debugging-find-and-fix-unnecessary-re-renders-with-real-examples-57l0</link>
      <guid>https://dev.to/purvisondarva/react-performance-debugging-find-and-fix-unnecessary-re-renders-with-real-examples-57l0</guid>
      <description>&lt;p&gt;Have you ever clicked a button in a React application and noticed that components you didn't touch were rendering again?&lt;/p&gt;

&lt;p&gt;Maybe a dashboard feels sluggish when updating a counter. Perhaps a large list becomes slow when typing into a search field. Or maybe you've added &lt;code&gt;React. memo, useMemo,&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt;, but the performance hasn't improved much.&lt;/p&gt;

&lt;p&gt;These problems are common in React applications, but the solution isn't to add memoization everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first step is to identify what is rendering, why it is rendering, and whether that rendering is actually expensive.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll investigate unnecessary React re-renders, reproduce a common problem, fix it with practical code, and learn how to verify whether the optimization works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What causes unnecessary re-renders in React?
&lt;/h2&gt;

&lt;p&gt;A React component can render again when its state changes, its parent renders, or its consumed context changes. External store subscriptions and other React mechanisms can also trigger updates.&lt;/p&gt;

&lt;p&gt;A render isn't necessarily a problem. React is designed to render components when needed.&lt;/p&gt;

&lt;p&gt;The real problem occurs when an interaction triggers expensive work that doesn't need to happen.&lt;/p&gt;

&lt;p&gt;For example, imagine a dashboard containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A counter that updates frequently.&lt;/li&gt;
&lt;li&gt;A user profile card.&lt;/li&gt;
&lt;li&gt;A large table containing hundreds of records.&lt;/li&gt;
&lt;li&gt;Several charts with expensive calculations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If updating the counter also causes expensive components to render repeatedly, the application may feel slower than necessary.&lt;/p&gt;

&lt;p&gt;Here are four common causes worth investigating:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe66m6fmq52b7sidi2sl4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe66m6fmq52b7sidi2sl4.png" alt=" " width="771" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The important distinction is that a component rendering again does not automatically mean it should be optimized. Start by finding measurable performance problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Reproduce an unnecessary re-render
&lt;/h3&gt;

&lt;p&gt;Let's consider a small dashboard. It has a counter and a user profile component.&lt;/p&gt;

&lt;p&gt;The profile doesn't depend on the counter, but it renders again whenever the counter changes.&lt;/p&gt;

&lt;p&gt;Here's the simplified implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { memo, useState } from "react";

const UserProfile = memo(function UserProfile({ user }) {
  console.log("UserProfile rendered");

  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h2&amp;gt;{user.name}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{user.role}&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  );
});

export default function Dashboard() {
  const [count, setCount] = useState(0);

  const user = {
    name: "Alex",
    role: "Frontend Engineer",
  };

  return (
    &amp;lt;main&amp;gt;
      &amp;lt;h1&amp;gt;Dashboard&amp;lt;/h1&amp;gt;

      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;
        Count: {count}
      &amp;lt;/button&amp;gt;

      &amp;lt;UserProfile user={user} /&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this looks reasonable. The profile component is already wrapped in memo, so why does it render again?&lt;/p&gt;

&lt;p&gt;The problem is this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const user = {
  name: "Alex",
  role: "Frontend Engineer",
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time &lt;code&gt;Dashboard&lt;/code&gt; renders, JavaScript creates a new object.&lt;/p&gt;

&lt;p&gt;Even though the object's properties are unchanged, its reference is different. React's default memoization comparison sees the new &lt;code&gt;user&lt;/code&gt; prop as changed, so &lt;code&gt;UserProfile&lt;/code&gt; cannot skip rendering because of &lt;code&gt;memo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a common source of confusion: identical object contents do not mean identical object references.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Find the problem with React DevTools Profiler
&lt;/h3&gt;

&lt;p&gt;Before changing the code, confirm what is happening.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://react.dev/learn/react-developer-tools" rel="noopener noreferrer"&gt;React Developer Tools&lt;/a&gt; extension includes Components and Profiler panels that help you inspect components and investigate rendering behavior.&lt;/p&gt;

&lt;p&gt;Here's a practical debugging workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open your application in the browser.&lt;/li&gt;
&lt;li&gt;Open React DevTools and select the Profiler tab.&lt;/li&gt;
&lt;li&gt;Start recording.&lt;/li&gt;
&lt;li&gt;Perform the interaction that feels slow, such as clicking the counter repeatedly.&lt;/li&gt;
&lt;li&gt;Stop recording and inspect the components involved in each commit.&lt;/li&gt;
&lt;li&gt;Look for expensive rendering work or components that repeatedly render without needing updated data.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Don't focus only on how many times a component renders. A component that renders frequently but takes very little time may not be worth optimizing.&lt;br&gt;
Instead, look for repeated work associated with a noticeably slow interaction.&lt;/p&gt;

&lt;p&gt;For a deeper investigation, React's &lt;a href="https://react.dev/reference/react/Profiler" rel="noopener noreferrer"&gt;Profiler API&lt;/a&gt; can measure rendering performance programmatically.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Fix the unstable object reference
&lt;/h3&gt;

&lt;p&gt;One possible fix is to memoize the user object with &lt;code&gt;useMemo&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { memo, useMemo, useState } from "react";

const UserProfile = memo(function UserProfile({ user }) {
  console.log("UserProfile rendered");

  return (
    &amp;lt;section&amp;gt;
      &amp;lt;h2&amp;gt;{user.name}&amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{user.role}&amp;lt;/p&amp;gt;
    &amp;lt;/section&amp;gt;
  );
});

export default function Dashboard() {
  const [count, setCount] = useState(0);

  const user = useMemo(
    () =&amp;gt; ({
      name: "Alex",
      role: "Frontend Engineer",
    }),
    []
  );

  return (
    &amp;lt;main&amp;gt;
      &amp;lt;h1&amp;gt;Dashboard&amp;lt;/h1&amp;gt;

      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;
        Count: {count}
      &amp;lt;/button&amp;gt;

      &amp;lt;UserProfile user={user} /&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the object reference stays stable between renders, as long as the dependencies don't change.&lt;/p&gt;

&lt;p&gt;When the counter updates, &lt;code&gt;UserProfile&lt;/code&gt; can skip rendering because its user prop has not changed.&lt;/p&gt;

&lt;p&gt;This works because two things are happening together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useMemo&lt;/code&gt; preserves the calculated object between renders.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;memo&lt;/code&gt; lets the child skip rendering when its props remain unchanged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using &lt;code&gt;useMemo&lt;/code&gt; without a reason isn't automatically beneficial. If the object is cheap to create and the child isn't memoized, the optimization may provide no meaningful improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A simpler alternative&lt;/strong&gt;: If the object is constant, move it outside the component entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const user = {
  name: "Alex",
  role: "Frontend Engineer",
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is often easier to understand when the data doesn't depend on props, state, or other reactive values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Fix unstable function props
&lt;/h3&gt;

&lt;p&gt;Objects aren't the only issue. Functions can also prevent memoized components from skipping renders.&lt;/p&gt;

&lt;p&gt;Consider this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const SaveButton = memo(function SaveButton({ onSave }) {
  console.log("SaveButton rendered");

  return &amp;lt;button onClick={onSave}&amp;gt;Save changes&amp;lt;/button&amp;gt;;
});

function Editor() {
  const [count, setCount] = useState(0);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;
        Counter: {count}
      &amp;lt;/button&amp;gt;

      &amp;lt;SaveButton onSave={() =&amp;gt; console.log("Saved")} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The inline function passed to &lt;code&gt;SaveButton&lt;/code&gt; is a new function reference on each render. Consequently, &lt;code&gt;memo&lt;/code&gt; cannot skip the child render based on unchanged props.&lt;/p&gt;

&lt;p&gt;If profiling confirms that this child is worth optimizing, &lt;code&gt;useCallback&lt;/code&gt; can preserve the function reference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { memo, useCallback, useState } from "react";

const SaveButton = memo(function SaveButton({ onSave }) {
  console.log("SaveButton rendered");

  return &amp;lt;button onClick={onSave}&amp;gt;Save changes&amp;lt;/button&amp;gt;;
});

export default function Editor() {
  const [count, setCount] = useState(0);

  const handleSave = useCallback(() =&amp;gt; {
    console.log("Saved");
  }, []);

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; setCount(count + 1)}&amp;gt;
        Counter: {count}
      &amp;lt;/button&amp;gt;

      &amp;lt;SaveButton onSave={handleSave} /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the callback reference stays stable, so &lt;code&gt;SaveButton&lt;/code&gt; can skip rendering when its props remain unchanged.&lt;/p&gt;

&lt;p&gt;Remember that &lt;code&gt;useCallback&lt;/code&gt; caches a function definition; it doesn't prevent the function from being created during the component's execution. React returns the cached function when its dependencies haven't changed.&lt;/p&gt;

&lt;p&gt;For more details, see the official &lt;a href="https://react.dev/reference/react/useCallback" rel="noopener noreferrer"&gt;useCallback documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Don't optimize every component
&lt;/h3&gt;

&lt;p&gt;It's tempting to wrap every component in &lt;code&gt;memo&lt;/code&gt; and every value in &lt;code&gt;useMemo&lt;/code&gt;. In practice, this can make code harder to read and maintain without improving the user experience.&lt;/p&gt;

&lt;p&gt;Before adding memoization, consider these alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep state close to where it is used&lt;/strong&gt;. If a search field only affects one section, its state usually doesn't need to live at the top of the entire application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid unnecessary Effects&lt;/strong&gt;. An Effect that updates state can create additional render cycles. If a value can be calculated from existing props or state during rendering, you may not need another state variable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use component composition&lt;/strong&gt;. Sometimes moving frequently changing state into a smaller component prevents unrelated parts of the interface from re-rendering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure expensive calculations&lt;/strong&gt;. Use &lt;code&gt;useMemo&lt;/code&gt; when a calculation is demonstrably expensive or when stable values are needed for an effective memoization boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check your React setup&lt;/strong&gt;. React Compiler can automatically memoize components, values, and functions when appropriately configured. This can reduce the need for manual &lt;code&gt;memo&lt;/code&gt;, &lt;code&gt;useMemo&lt;/code&gt;, and &lt;code&gt;useCallback&lt;/code&gt; in supported applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can learn more in the official &lt;a href="https://react.dev/reference/react/memo" rel="noopener noreferrer"&gt;React memo documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Verify that the optimization worked
&lt;/h3&gt;

&lt;p&gt;After making a change, record the same interaction again using React DevTools Profiler.&lt;/p&gt;

&lt;p&gt;Compare the results before and after the change:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffpvfjvvj1onfuza3zqa3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffpvfjvvj1onfuza3zqa3.png" alt=" " width="711" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Avoid inventing performance percentages. The improvement depends on your application, the component tree, the data, and the environment.&lt;/p&gt;

&lt;p&gt;Also remember that development-mode measurements can differ from production behavior. React Strict Mode may intentionally invoke rendering logic more than once during development to help reveal bugs. For meaningful performance conclusions, use appropriate profiling tools and repeatable test conditions.&lt;/p&gt;

&lt;p&gt;The goal isn't to achieve the fewest possible renders. It's to remove unnecessary work without making the code more complicated than it needs to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  A practical checklist for debugging React performance
&lt;/h2&gt;

&lt;p&gt;When a React application feels slow, use this sequence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reproduce the slow interaction consistently.&lt;/li&gt;
&lt;li&gt;Record the interaction with React DevTools Profiler.&lt;/li&gt;
&lt;li&gt;Identify which components consume meaningful rendering time.&lt;/li&gt;
&lt;li&gt;Check whether props, state, or context actually need to change.&lt;/li&gt;
&lt;li&gt;Look for unstable object and function references.&lt;/li&gt;
&lt;li&gt;Consider simpler state placement or component composition.&lt;/li&gt;
&lt;li&gt;Apply memoization only when it addresses the identified problem.&lt;/li&gt;
&lt;li&gt;Profile again and verify both performance and correctness.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Following this process makes React performance debugging more systematic and helps avoid premature optimization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Unnecessary React re-renders are worth investigating when they cause measurable performance problems, but rendering itself isn't the enemy.&lt;/p&gt;

&lt;p&gt;Start with the React DevTools Profiler, identify the work that matters, and then choose the simplest fix. Stable props, sensible state placement, component composition, and targeted memoization can all help when used for the right reasons.&lt;/p&gt;

&lt;p&gt;Optimizing rendering is just one part of building fast, maintainable React applications. A well-structured component architecture and thoughtful performance decisions also play an important role. If you're exploring these aspects further, learn more about &lt;a href="https://www.encodedots.com/reactjs-development" rel="noopener noreferrer"&gt;ReactJS development&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;The takeaway is simple: measure first, optimize second, and measure again.&lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
What's the most common React performance issue you've encountered in a real project: unstable props, expensive calculations, state management, or something else? Share your experience in the comments.&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>react</category>
      <category>reactapplication</category>
      <category>backend</category>
    </item>
  </channel>
</rss>
