DEV Community

Cover image for When do (and don’t) children re-render in React?
Vardan Hakobyan
Vardan Hakobyan

Posted on

When do (and don’t) children re-render in React?

When writing React components, we always strive to keep optimization in mind. It’s always useful to know when unnecessary re-rendering occurs in our code, especially if we have heavy components in our app.

So let’s dive in and see what happens with child components when their parents re-render.

In most cases we use child components by their name. Let’s examine the following code:

import { useState } from "react";

const Parent = () => {
  const [number, setNumber] = useState(0);

  console.log('<Parent> renders');

  return (
    <div>
      Number is {number}
      <div>
        <button onClick={() => setNumber(prev => prev + 1)}>
          Increment
        </button>
      </div>
      <Child />
    </div>
  );
};

const Child = () => {
  // Some time-consuming logic
  console.log('<Child> renders');

  return (
    <div>
      <div>Child component</div>
    </div>
  );
};

const App = () => {
  return (
    <Parent />
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Every time the button is pressed, the state of the <Parent /> component changes and it re-renders, which causes re-rendering <Child /> component as well.

How can we prevent unnecessary re-renders for the Child component? Let’s explore two approaches to achieve this.

Approach 1: Our friend React.memo

If we wrap the Child component into React.memo, the Parent’s re-render will not cause the Child to re-render.

Quick reminder — React.memo memoizes components, ensuring they are not re-rendered when their props remain unchanged.

import { memo, useState } from "react";

const Parent = () => {
  const [number, setNumber] = useState(0);

  console.log('<Parent> renders');

  return (
    <div>
      Number is {number}
      <div>
        <button onClick={() => setNumber(prev => prev + 1)}>
          Increment
        </button>
      </div>
      <Child />
    </div>
  );
};

const Child = memo(() => {
  // Some time-consuming logic
  console.log('<Child> renders');

  return (
    <div>
      <div>Child component</div>
    </div>
  );
});

const App = () => {
  return (
    <Parent />
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Approach 2: Using {children} prop

We can use {children} prop to achieve the same result. Let’s see it in action.

{children} prop allows the parent component to accept and render its children components dynamically.

import {useState} from "react";

const Parent = ({children}) => {
  const [number, setNumber] = useState(0);

  console.log('<Parent> renders');

  return (<div>
    Number is {number}
    <div>
      <button onClick={() => setNumber(prev => prev + 1)}>
        Increment
      </button>
    </div>
    {children}
  </div>);
};

const Child = () => {
  // Some time-consuming logic
  console.log('<Child> renders');

  return (
    <div>
      <div>Child component</div>
    </div>
  );
};

const App = () => {
  return (
    <Parent>
      <Child />
    </Parent>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Using children prop here makes the code easier to follow, and also removes extra clutter coming from memo.

While these two approaches help us remove unnecessary re-rendering child components, bear in mind that we don’t need to blindly optimize everything. If our component doesn’t involve heavy calculations or other performance bottlenecks, we usually shouldn’t prematurely optimize it. Always strive to keep components small and easy to follow, and optimize them only when necessary.

Thanks for reading! If you have any notes, remarks or questions, please share them in the comments.

Stay updated with the latest JavaScript and software development news! Join my Telegram channel for more insights and discussions: TechSavvy: Frontend & Backend.

Top comments (0)