You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止Accordion组件内AccordionBody递归重渲染且保留下拉动画

解决全量重渲染问题

所有<AccordionItem/>跟着父组件重渲染的核心原因是React默认父组件状态更新时所有子组件都会触发重渲染,你可以通过以下方式优化:

  • 用React.memo包裹<AccordionItem/>组件,默认会对props做浅比较,只有props变化时才会触发组件重渲染
  • 父组件传递给<AccordionItem/>的回调函数(比如切换激活状态的方法)必须用useCallback包裹,避免每次父组件渲染都生成新的函数引用,导致memo失效
  • 所有传递给<AccordionItem/>的引用类型props(比如自定义样式、配置对象)如果是固定值,要么抽成组件外的常量,要么用useMemo缓存

解决懒渲染与动画兼容问题

你之前直接用{active && <AccordionBody />}导致动画失效的原因是:CSS高度过渡需要目标元素真实存在才能计算尺寸执行动画,切换active的同时直接挂载/卸载内容,会导致过渡触发时内容不存在,自然看不到动画效果,你可以用以下方案兼容两者:

方案1:优先推荐的grid过渡方案

这个方案不需要手动计算内容高度,兼容性也覆盖绝大多数现代浏览器,同时支持懒渲染:

  1. 给<AccordionBody/>外层加一个容器元素,用CSS grid的行高过渡实现展开收起动画,避免依赖内容渲染后的高度计算
  2. 新增hasOpened状态,仅当条目第一次被激活时才渲染<AccordionBody/>的实际内容,未激活过的条目不会渲染主体,降低首次渲染开销
  3. 如果需要在条目关闭后卸载主体释放内存,可以监听过渡结束事件,等收起动画执行完成后再卸载内容

示例代码如下:

// AccordionItem 组件(已用memo包裹避免无效重渲染)
const AccordionItem = React.memo(({ itemId, isActive, onToggle, header, body }) => {
  // 仅当第一次激活后才渲染主体内容
  const [hasRenderedBody, setHasRenderedBody] = useState(isActive);

  useEffect(() => {
    if (isActive) {
      setHasRenderedBody(true);
    }
  }, [isActive]);

  // 过渡结束后如果是关闭状态,卸载主体
  const handleTransitionEnd = (e) => {
    if (e.propertyName === 'grid-template-rows' && !isActive) {
      setHasRenderedBody(false);
    }
  };

  return (
    <div className="accordion-item">
      <div className="accordion-header" onClick={() => onToggle(itemId)}>
        {header}
      </div>
      <div 
        className={`accordion-body-wrap ${isActive ? 'active' : ''}`}
        onTransitionEnd={handleTransitionEnd}
      >
        {hasRenderedBody && <div className="accordion-body">{body}</div>}
      </div>
    </div>
  );
});

对应CSS样式:

.accordion-body-wrap {
  display: grid;
  grid-template-rows: 0fr;
  overflow: hidden;
  transition: grid-template-rows 0.3s ease;
}
.accordion-body-wrap.active {
  grid-template-rows: 1fr;
}
.accordion-body {
  min-height: 0;
}

方案2:兼容旧浏览器的手动计算高度方案

如果需要兼容不支持grid过渡的旧浏览器,可以手动计算内容的scrollHeight:

  • 激活条目时先挂载主体内容,读取scrollHeight后设置容器高度为对应值,执行过渡
  • 关闭条目时先把容器高度设为0,等过渡结束后再卸载主体内容

内容的提问来源于stack exchange,提问作者Reinier68

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 12:36:03