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

React中如何获取动态子元素高度并解决ref获取时的无限循环问题

问题根因

  1. 函数组件无法直接接收ref属性:直接给TimelineElement传递ref属性会被React过滤,不会进入props,所以子组件无法拿到ref绑定到DOM,导致你拿不到元素引用。
  2. 无限循环触发原因:
    • 你在组件顶层每次渲染都会重新执行data.map生成新的refs数组和components数组,两个数组的引用每次渲染都不一样
    • 你把这两个变量放到了useLayoutEffect的依赖数组里,每次渲染都会触发effect执行,effect里的setHeights又会触发组件重新渲染,形成死循环。

修复后完整代码

import React, { forwardRef, useRef, useLayoutEffect, useState } from 'react';

// 用forwardRef包裹子组件,第二个参数接收外部传入的ref
const TimelineElement = forwardRef<HTMLElement, {data: TimelineData}>(({data}, ref) => {
  return <div className="timeline-element" ref={ref}>
    <div className="timeline-element-title">{data.title}</div>
    <div className="timeline-element-details">{data.details}</div>
  </div>;
});
TimelineElement.displayName = 'TimelineElement';

export const Timeline : React.FC<{data: TimelineData[]}> = ({data}) => {
  // 用useRef存储ref数组,引用稳定不会每次渲染变化
  const refs = useRef<(HTMLElement | null)[]>(new Array(data.length).fill(null));
  const [heights, setHeights] = useState<number[]>([]);

  useLayoutEffect(() => {
    // 计算所有元素的高度
    const newHeights = refs.current.map(el => el?.offsetHeight || 0);
    setHeights(newHeights);
    // 仅当data变化时才需要重新计算高度
  }, [data]);

  const components = data.map(
    (r, i) => <TimelineElement 
      ref={(el) => refs.current[i] = el} 
      data={r} 
      key={r.id /* 建议给每个元素加唯一key,避免react渲染异常 */}
    />
  );

  return <div className="timeline">
    {components}
    {JSON.stringify(heights)}
  </div>;
}

补充说明

  • 如果你的data数组长度会动态变化,可以在useLayoutEffect里先同步更新refs.current的长度,避免下标不匹配的问题。
  • 给列表元素加唯一key是React列表渲染的最佳实践,可以避免很多异常渲染问题,建议用TimelineData里的唯一字段作为key,不要用下标。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 22:54:05