Reinier68
Reinier68

Reputation: 3324

How do I stop recursive renders of a AccordionBody component inside a Accordion?

I got an <Accordion/> component that can hold multiple <AccordionItem/> components. I prepared a codesandbox demo here.

The problem currently is that on the initial render, all the <AccordionItem/> components are rendered (check the console logs in the codesandbox example). So depending on the size of the array the <Accordion/> component uses to render all the items, this means a lot of initial renders. Also, everytime a <AccordionItem/> is openend, all the items inside the <Accordion/> are re-rendered again. For performance sake, this might not be an issue with just a few items. However, in my application, this can be 10 <AccordionItem/> components that have a nested accordion themselves that are constantly getting rerendered everytime a user opens one. So this can mean that everytime a user opens an item, 50 re-renders happen or so.

I've already tried a bunch of things to eliminate this. For instance, the accordion relies on a active Boolean. So only rendering the body of the accordion when this active boolean is true eliminates the problem, like so:

<div className="answer">{active && <AccordionBody />}</div>

However, the dropdown animation stops working and the body is not opened completely. What can I do to only render the clicked on <AccordionItem/> and its body while maintaining the nice dropdown animation?

Upvotes: 1

Views: 821

Answers (1)

Drew Reese
Drew Reese

Reputation: 203522

Generally speaking you should let React rerender when it needs to rerender.

You are incorrectly console logging in the function body of AccordionBody component as an unintentional side-effect, however, even after moving the console logs into a useEffect (recall that useEffect hook is called once per render) the AccordionBody is still rerendered again as part of the Accordion component rerendering the faqs array when state updates.

React components rerender when either state or props update, or when their parent component rerenders. When a component rerenders it necessarily rerenders its entire component subtree (i.e. its children).

These additional rerenders are generally inexpensive and not necessarily an issue alone. It really depends on what each component being rerendered does when it's rerendered. Don't prematurely optimize!

Tools that you can use to help performance, if there is an issue, is to memoize props, i.e. using useMemo or useCallback to provide stable prop references.

You can also memoize components as well with the memo Higher Order Component. Given the component renders the same result from the same props, you can memoize the rendered result. In your example codesandbox this eliminates the duplicate rerenders.

AccordionBody

import React, { memo, useEffect } from "react";

function AccordionBody() {
  useEffect(() => {
    console.log("AccordionBody rendered"); // <-- log once per render
  });

  return (
    <div>
      <div>All of the AccordionBody's are rendered</div>
      <div>So everytime an accordion item is openend</div>
      <div>The AccordionBody component gets rendered 3 times</div>
      <div>1 for every faq in the faqs array</div>
      <div>How will this impact performance in a large array?</div>
      <div>
        How do I stop these recursive re-renders, while maintaining the
        animation?
      </div>
    </div>
  );
}

export default memo(AccordionBody); // <-- memoize render result

Edit how-do-i-stop-recursive-renders-of-a-accordionbody-component-inside-a-accordion

Note: Don't prematurely optimize, but only look to optimize when you find a rendering or performance issue. This will typically be on a case-by-case basis.

Upvotes: 1

Related Questions