How React Re-Renders a Component

Understand in Depth how Re-Render works in React

Ashutosh Kumar
Level Up Coding

--

Photo by Lautaro Andreani on Unsplash

In a previous article, I explained how React renders a component, how React DOM works, and how reconciliation happens behind the scenes. If you haven't already, please read that article before continuing with this one, as we will be delving much deeper into React re-renders.

To begin, it's important to have a grasp of some fundamental concepts of React. Ask yourself these questions:

  1. What is a React Component?
  2. What is a React Element?
  3. How does React Lifecycle work?
Photo by julien Tromeur on Unsplash

What is a React Component?

A simple React Component looks something like this

function About() {
return <h2>Welcome to my Page</h2>;
}

This looks like a regular Javascript function. Does that mean a React Component is essentially a function?

Partly correct. However, the Component is distinguished from a regular function by its return type. The Component returns something that we call a React Element that tells React what should be rendered on the screen. These React elements are then converted into DOM elements and sent to the browser for rendering on the screen.

What is a React Element?

A React Element is an object that defines a component to be rendered on the screen.

As explained in my previous article the HTML-like code that we see inside a component is nothing but a syntactical sugar for JSX. Babel compiles JSX down to React.createElement() call that create a React Element to be returned by the Component.

Before compilation:

function About() {
return <h2>Welcome to my Page</h2>;
}

After Compilation:

function About() {
return React.createElement("h2", null, "Welcome to my Page");
}

In simple terms, A react element is an object that describes the DOM nodes that you’d like ReactDOM.render() to create. The render() function returns a DOM tree of react elements behind the scenes.

This is the main reason why React is called Declarative

Syntax of React Element:

React.createElement(type, props, children)

Going back to our example:

function About() {
return React.createElement("h2", null, "Welcome to my Page");
}

Behind the scenes, the React.createElement returns an object which looks like this

{
type: "h2",
key: null,
ref: null,
props: {
children: "Welcome to my Page"
},
...
}

How does React Lifecycle work?

A React component undergoes three phases in its lifecycle: mounting, updating, and unmounting.

  1. Mounting Phase: When a component first appears on the screen, we call it mounting. A new component is created and inserted into the DOM (initial render)
  2. Updating Phase: When the component updates or re-renders. This reaction is triggered when the state is updated and React updates an already existing component with some new information.
  3. Unmounting Phase: The last phase of the component’s lifecycle, It has been discovered that the component is no longer necessary and has therefore been removed from the DOM.

Now that you have a strong understanding of React internals, let’s move on to the most important question

How does React Re-Render work?

This is the most important topic in React if you want to understand React Performance. We know that when a state change occurs in React, the component updates, and a process called reconciliation happens in the background.

What Re-Render means is the re-execution of the functions and calculating the changes that need to be updated In the browser (Actual DOM) in an optimal way.

The render() function returns a DOM tree of react elements behind the scenes called Virtual DOM. This Virtual DOM is compared with the previous Virtual DOM, before re-rendering, to determine which elements in the actual DOM need to be updated. This comparison helps React to efficiently update only the necessary parts of the DOM, improving the performance of the application.

Photo by JESHOOTS.COM on Unsplash

Let’s Understand by an example how this comparison (before render vs after render) works

function Child() {
console.log("Child re-render");

return <h2>Hello World</h2>;
}

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

const addFn = () => {
setCount(count + 1);
};

console.log("Parent re-render " + count);

return (
<div>
<Child />
<button onClick={addFn}>Add</button>
</div>
);
}

In the above example, we have two Components a Parent and a Child, The Parent Component has a state that gets changed with the Button Click. The Child is a stateless component that never changes and is nested inside the Parent Component

What do you think will be the output when the button is clicked?

Parent re-render 1
Child re-render

Answer: The Parent and Child Component both re-render

Why does the Child re-render?

The Child Component before and after state change remains the same but it still gets executed. Let’s understand step by step what is happening behind the scenes with React after the button is clicked.

The state variable count is changed from 0 to 1 and the Parent Component is flagged for Update. We know that during Re-Render the function gets re-executed which means everything inside the function Parent is called/rebuilt again.

What this means for our Child Component is a new reference of Child is created and returned as React Element. Remember Child Component is a JSX syntax for React.createElement function which returns an Object.

Parent Component after Compilation:

React.createElement("div", null, React.createElement(Child, null), 
React.createElement("button", { onClick: ... }, "Add"));

The Parent returns an Object which looks like

{
type: 'div'
props: {
children: [
{
type: Child //function reference returned by React.createElement(Child, null)
props: {}
},
{
type: 'button',
props: {
onClick: addFn
children: "Add"
}
}
]
}
}

Hence every time the Parent function executes it returns a new reference of the Child as React.CreateElement(Child, null) gets called, The Component returns React Elements which are nothing but Javascript Objects. So whenever the Parent function gets called (or re-rendered), this object will be re-created with a new Object reference.

React then Compares this Object returned by the Parent Component before and after re-rendering, if both of them are the same, React will skip re-rendering this Component and its nested components.

The important thing to note here is that React does a shallow comparison that looks like this

Object.is(ElementBeforeRerender, ElementAfterRerender)

In our case, the Child Component reference is always going to be new (different from before re-render) and the result is false hence react Re-Renders the Child Component also.

How do we stop the Child Component from re-rendering?

There are multiple ways to solve this, One solution is to use React.Memo but there is a simple solution by passing the Child Component as a prop, This way the Child Component reference is not re-created whenever the Parent Component is re-rendered as a prop does not change.

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

console.log("parent re-render: " + count);

const addFn = () => {
setCount(count + 1);
};

return (
<div className="App">
{props.children}
<button onClick={addFn}>Add</button>
</div>
);
}

root.render(
<Parent>
<Child />
</Parent>
);

I have also explained about memoization solutions in my previous article:

--

--

Self-taught Developer | Tech Blogger | Aim to Help Upcoming Software Developers in their Journey