如何停止 Accordion 中 AccordionBody 组件的递归渲染?

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

我有一个 <Accordion/> 组件可以容纳多个 <AccordionItem/> 组件。我准备了一个codesandbox demo here.

目前的问题是,在初始渲染时,所有 <AccordionItem/> 组件都被渲染(检查 codesandbox 示例中的控制台日志)。因此,根据 <Accordion/> 组件用于渲染所有项目的数组大小,这意味着需要进行大量初始渲染。此外,每次 <AccordionItem/> 是 openend 时,<Accordion/> 中的所有项目都会再次重新呈现。为了性能起见,这可能不是只有少数项目的问题。但是,在我的应用程序中,这可以是 10 <AccordionItem/> 个组件 ,它们本身具有嵌套的手风琴 ,每次用户打开一个时,它们都会不断重新呈现。所以这可能意味着每次用户打开一个项目时,都会发生 50 次左右的重新呈现。

我已经尝试了很多方法来消除这个问题。例如,手风琴依赖于 active 布尔值。因此,仅当此 active 布尔值为真时渲染手风琴主体即可消除问题,如下所示:

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

但是,下拉动画停止工作,主体没有完全打开。我该怎么做才能在保持漂亮的下拉动画的同时只渲染点击的 <AccordionItem/> 及其主体?

一般来说,你应该让 React 在需要重新渲染时重新渲染。

您错误地将 AccordionBody 组件的函数体中的控制台日志记录为无意的副作用,但是,即使将控制台日志移至 useEffect 召回useEffect 钩子每次渲染调用一次 ) AccordionBody 仍然作为 Accordion 组件的一部分再次重新渲染,当状态更新时重新渲染 faqs 数组。

React 组件在 state 或 props 更新时重新渲染,或者当它们的父组件重新渲染时。当一个组件重新渲染时,它必然会重新渲染它的整个组件子树(即它的子树)。

这些额外的重新渲染通常成本低廉,不一定是一个单独的问题。这实际上取决于每个被重新渲染的组件在重新渲染时做了什么。 不要过早优化!

如果有问题,您可以用来帮助提高性能的工具是记忆道具,即使用 useMemouseCallback 来提供稳定的道具参考。

您还可以使用 memo 高阶组件记忆组件。鉴于组件从相同的道具渲染相同的结果,您可以记住渲染的结果。在您的示例 codesandbox 中,这消除了重复的重新渲染。

手风琴体

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

注意:不要过早优化,只有在发现渲染或性能问题时才考虑优化。这通常视具体情况而定。