Why React Re-Renders?

Kamal Neupane

Kamal NeupaneAug 16, 2025

8 min read1577 words

why-react-rerender.jpg

Getting started with React isn’t as difficult as it might seem—the initial learning curve is fairly gentle, and you can quickly build useful things with it. The real challenge begins when you dig deeper into React’s internals and try to optimize performance, especially by controlling unnecessary re-renders of components. Even without fully understanding how rendering works, you can still ship plenty of features. But sooner or later, if your senior developers review your pull requests, you’ll probably see comments about preventing extra re-renders. This blog will focus on that exact part of React: understanding re-rendering and some misconception regarding it.


If you've ever worked in a midsize or large React application, you've probably noticed that components re-render a lot. In some cases, this can lead to performance problems, where a small change near the top of the application causes a ton of unrelated components to re-render.


To start with

Almost everyone who has worked with React might know that calling a state setter function (e.g., setUser, setCount) triggers a re-render. This re-render allows React to take a fresh snapshot of how the UI should look based on the updated state value, and then update the DOM accordingly.


The basic rule

So, let's start with a fundamental truth: Every re-render in React starts with a state change. It's the only “trigger” in React for a component to re-render.*

Now, that probably doesn't sound right... after all, don't components re-render when their props change?

Here's the thing: when a component re-renders, it also re-renders all of its descendants.


In this example, we have 3 components: App at the top, which renders TodoApp, which renders TaskList.

In React, every state variable is attached to a particular component instance. In this case, we have a single piece of state, tasks, which belongs to the TodoApp component.

Whenever this state changes (like when a new task is added), TodoApp re-renders. And because TaskList is being rendered by TodoApp, it will re-render as well to display the updated list of tasks.

Here's a GIF that shows this mechanic in action. Clicking the “Add Task” button will trigger a state change:

Component Re-rendering visualization for the above example


From the example above, if someone thinks “The entire app re-renders whenever a state variable changes.” this is a misconception—the example itself shows that's not the case.


Some developers might believe that every state change in React forces an application-wide render, but this isn't true. Re-renders only affect the component that owns the state + its descendants (if any). The App component, in this example, doesn't have to re-render when the tasks state variable changes.


React's “main job” is to keep the application UI in sync with the React state. The point of a re-render is to figure out what needs to change.


Let's consider the “Todos” example above. When the application first mounts, React renders all of our components and comes up with the following sketch for what the DOM should look like:

initial-dom.html

<main>
  <ul>
    <!-- empty, no tasks yet -->
  </ul>
  <button>
    Add Task
  </button>
</main>
<footer>
  <p>Made with ❤️ by Todo Masters</p>
</footer>

When the user clicks the Add Task button, the tasks state variable changes from an empty array [] to a new array with one item ["Task 1"]. How does this affect the UI? Well, that’s what we can see by doing another render!

React re-runs the code for the TodoApp and TaskList components, and we generate a new picture of the DOM we want:

dom-after-initial-button-click.html

<main>
  <ul>
    <li>
      <span class="bullet">📌</span> Task 1
    </li>
  </ul>
  <button>
    Add Task
  </button>
</main>
<footer>
  <p>Made with ❤️ by Todo Masters</p>
</footer>

Each render is like a snapshot, a photo that tells us what the UI should look like, based on the current application state.


React then plays a “find the differences” game to figure out what’s changed between snapshots. In this case, it notices that our <ul> went from being empty to containing a new <li> with “Task 1”, so it inserts that new list item into the DOM. Satisfied that its work is done, React settles back and waits for the next state change. This is what we’ve called the core React loop.


Our tasks state is associated with the TodoApp component. Because data can’t flow “up” in a React application, we know that this state change can’t possibly affect <App />. And so we don’t need to re-render that component.


But we do need to re-render TodoApp’s child, TaskList. This is the component that actually displays the tasks state. If we don’t render it, we won’t know that our <ul> should now include a new <li> for “Task 1”. We need to include this component in our sketch.


The point of a re-render is to figure out how a state change should affect the user interface. And so we need to re-render all potentially affected components, to get an accurate snapshot.


It's not about the props

In the past, I used to think that a component re-renders because its props change. But that also turned out to be a misconception.


In the code below, our TodoApp has been given a brand new component, Decoration:

Now, our Decoration component doesn't depend on tasks, so it probably won't re-render when the tasks state changes, right?


Not quite.

Decoration Component Re-rendering without its props getting changed


When a component re-renders, it tries to re-render all descendants, regardless of whether they're being passed a particular state variable through props or not.


Now, this seems counter-intuitive... If we aren't passing tasks as a prop to <Decoration>, why would it need to re-render?


Because it's hard for React to know, with 100% certainty, whether <Decoration> depends, directly or indirectly, on the tasks state variable. Let's go through one example that proves this point.


In an ideal world, React components would be “pure”. A pure component is one that always produces the same UI when given the same props.


But, in the real world, many of our components are impure. It's surprisingly easy to create an impure component. For example:

impure-component.jsx

function RandomNumber() {
  const num = Math.random();
 
  return (
    <p>Here’s a random number: {num}</p>
  );
}
 

This component will display a different value whenever it's rendered, since it relies on a random number!


So, if we assumed that components only re-render when their props change, we'd conclude that this RandomNumber component would never show a new value when its parent re-renders due to a state change — which is clearly not true.


React’s top priority is to keep the UI in sync with application state. So React errs on the side of extra renders—it doesn’t want to risk showing the user a stale UI.


So, to go back to our misconception: props don’t independently trigger re-renders. Our <TodoList> component isn’t re-rendering because a prop changed; it re-renders because its parent rendered after a state update.


When a component re-renders, because one of its state variables has been updated, that re-render will cascade all the way down the tree, in order for React to figure out what the new snapshot should look like.


Test your knowledge

This section requires you to have knowledge of memoization and also you should be efficient to use useMemo and useCallback hook. If you are not comfortable with it then feel free to skip this section.

In the following case, does the handleGetText function need to be memoized with useCallback? Would using useCallback provide any real benefits here, or would it just add unnecessary overhead? See the case when count state is updated.


Well, the answer is.

Useless useCallback. Count state change is causing CopyInput component to re-render.


We are preserving the reference of handleGetText by wrapping it inside useCallback but still when count state changes CopyInput function is getting re-rendered. So, its not the props change that cause a component to re-render.


Whats the solution here?


We need to explicitly tell react to not re-render react when its props is same across re-render. Hey, react this component is pure! It doesn't need to re-render unless its props or state changes. It will always return the exact same UI when given the same props + state.


How can we do it?


By wrapping, CopyInput component inside React.memo.


When App re-renders, React will try to re-render the child CopyInput, but CopyInput steps in and says “None of my props have changed, and so I won't be re-rendering this time.”


CopyInput component is not re-rendering in this case when count state changes.