【问题标题】:Get height of dynamic child elements in React获取 React 中动态子元素的高度
【发布时间】:2021-12-12 22:32:33
【问题描述】:

我想使用其他一些元素的高度来动态设置一些元素的高度。不幸的是,我不知道如何获取对“源”对象的引用并且总是陷入无限循环。这是我到目前为止所尝试的。 TimelineData 只是一个简单的对象,具有一些属性来填充元素:

const TimelineElement : React.FC<{data: TimelineData, ref : any}> = ({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>;
}

export const Timeline : React.FC<{data: TimelineData[]}> = ({data}) => {
  const refs = data.map(() => createRef<HTMLElement>());
  const components = data.map(
    (r, i) => <TimelineElement ref={(el : any) => refs[i] = el} data={r}/>
  );
  const [heights, setHeights] = useState<any[]>([]);
  useLayoutEffect(() => {
    setHeights(refs.map(
      ref => ref.current?.tagName
    ));
  }, [components, refs]);
  return <div className="timeline">
    {components}
    {JSON.stringify(heights)}
  </div>;
}

【问题讨论】:

    标签: javascript html reactjs typescript


    【解决方案1】:

    使用createRef 将在每个渲染上创建一个新的引用,这将导致执行setHeights 这将导致重新渲染等等,这里几乎没有解决方法,但我建议使用@987654323 @hook 而不是,它将返回相同的 ref

    更新:

    除了上述答案之外,为了处理您的情况,您可以将多个元素存储在一个创建的引用中,而不是在循环中创建引用数组:

    const refs = useRef([]);
    ...
    ...
    <TimelineElement ref={(el: any) => (refs.current[i] = el)} data={r} />
    ...
    ...
    setHeights(refs.current?.map((element) => element.tagName));
    

    在这里,您解决了 refs 的问题,但是由于另一部分,您将拥有相同的无限循环,您的 useLayoutEffect 依赖于 components,这将在每次渲染时获得一个新值,在这里我们可以使用useMemo 解决这个问题:

    const components = useMemo(() => data.map(
        (r, i) => <TimelineElement ref={(el : any) => refs[i] = el} data={r}/>
      ), [refs]);
    

    还有一点需要修复,不要给功能组件ref,可以重命名为不同的名字:

    <TimelineElement elementRef={(el: any) => (refs.current[i] = el)} data={r} />
    

    所以在这些点之后,这里是完整的代码:

    const TimelineElement: React.FC<{ data: TimelineData; elementRef: any }> = ({
      data,
      elementRef
    }) => {
      return (
        <div className="timeline-element" ref={elementRef}>
          <div className="timeline-element-title">{data.title}</div>
          <div className="timeline-element-details">{data.details}</div>
        </div>
      );
    };
    
    export const Timeline: React.FC<{ data: TimelineData[] }> = ({ data }) => {
      const refs = useRef([]);
      const [heights, setHeights] = useState<any[]>([]);
    
      const components = useMemo(
        () =>
          data.map((r, i) => (
            <TimelineElement elementRef={(el: any) => (refs.current[i] = el)} data={r} />
          )),
        [refs]
      );
    
      useLayoutEffect(() => {
        setHeights(refs.current?.map((element) => element.tagName));
      }, [components]);
    
      return (
        <div className="timeline">
          {components}
          {JSON.stringify(heights)}
        </div>
      );
    };
    

    最后一点:不要忘记在map() 中的元素中添加唯一的key ;)

    【讨论】:

    • 我不能在动态循环中使用useRef
    • @Chrisstar,很抱歉没有从第一次发布完整的答案,请检查更新的答案:)
    • 你好@Zac我真的很感谢你的回答,至少无限递归已经消失了(我怀疑useMemo已经做到了,这本身就是有问题的,因为这不是它应该如何使用根据反应文档)。现在的问题是当 useLayoutEffect 被称为 refs.current 时仍然是一个空数组,这无助于我解决我原来的问题
    猜你喜欢
    • 1970-01-01
    • 2017-07-20
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    • 2011-08-05
    • 2020-06-30
    • 1970-01-01
    相关资源
    最近更新 更多