Prevent re-renders with Component Composition in React

Prevent re-renders with Component Composition in React

Learn to reduce re-renders with component composition

For those seeking optimal performance, minimizing re-renders is a common objective. Let's discuss how can we effectively reduce re-renders with Component Composition.

Before delving into the main topic of reducing re-renders, let's take a moment to quickly understand what re-rendering entails.

What is re-render?

When working with React, it is important to consider two key aspects: the initial rendering and re-rendering.

Initial Render: In React, the initial render refers to the process of rendering components and their child elements for the first time. When a React application starts or a component is first mounted, React goes through the initial render phase to create the initial UI.

Re-render: A re-render in React refers to the subsequent rendering of a component that is already visible on the screen, occurring after the initial render. It updates the component's appearance or behavior in response to changes in its state or props.

What is component composition in React?

Just like we can pass props to a React component, we can also pass components as props to other components. This is called component composition in React. It allows us to create more flexible and reusable code by combining smaller components to build larger, more complex ones. By using component composition, we can make our code easier to maintain and scale our React applications effectively.

How to prevent re-renders with component composition?

Let's first have a look at the code below and try to understand what will happen to the <InnocentComponent /> if the state in the App component changes.

const App = () => {
  const [isOpen, setIsOpen] = useState(false);
  return (
      <Container>
        <button onClick={() => setIsOpen(!IsOpen)}>Open</button>
        {IsOpen && <Modal />}
        <InnocentComponent />
    </Container>
  );
};

As you already know in React, when the states of a parent component change, the parent component, along with all of its children, undergoes re-rendering. Consequently, even if our InnocentComponent is not directly involved with the state changes, it will still be re-rendered.

How can we optimize this in a way that prevents our InnocentComponent from being re-rendered?

We also know that when the children are re-rendered, the parent is not affected. So, we can move the state logic one level down the App to a separate component, let's call it <ModalButton />. Now let's have a look at the code:

const ModalButton = () => {
    const [isOpen, setIsOpen] = useState(false);
    return (
        <button onClick={() => setIsOpen(!IsOpen)}>Open</button>
        {IsOpen && <Modal />}
    );        
};


const App = () => {
  return (
      <Container>
        <ModalButton />
        <InnocentComponent />
    </Container>
  );
};

Now our <InnocentComponent /> is safe and will not be re-rendered each time you play with the modal.

The problem is solved by pushing the state one level down. However, there are other ways to approach this issue.

Let's consider a scenario where the parent component contains an onScroll event, and the state is altered as a result of this event.

Have a look at the code snippet below:

const App = () => {
  const [scrollValue, setScrollValue] = useState({})
  return (
      <Container onScroll={(e) => setScrollValue(e)}>
        <InnocentComponent />
    </Container>
  );
};

Likewise our <InnocentComponent /> will keep on re-rendering when you scroll the page. How to optimize this, so that we can prevent re-renders? Let's have a look

const ScrollComponent = ({ children }) => {
    const [scrollValue, setScrollValue] = useState({});
    return (
        <Container onScroll={(e) => setScrollValue(e)}>
            {children}
        </Container>
    );
};

const App = () => {

  return (
      <ScrollComponent>
        <InnocentComponent />
    </ScrollComponent>
  );
};

Now, the <InnocentComponent /> will not undergo re-rendering. This is because the component responsible for managing the state logic, <ScrollComponent /> receives the children as props and returns them as they are. From the perspective of <ScrollComponent />, the children are treated as simple props and remain unaffected by any changes in state.

Thank you for reading. By exploring and addressing a specific topic, I aim to create a valuable resource that I can refer back to when encountering similar challenges in the future. In the ever-evolving landscape of web development, sharing knowledge and documenting solutions contributes to the growth and improvement of the community. Stay curious and keep building amazing web applications!