【问题标题】:How to get element height and width from ReactNode?如何从 ReactNode 获取元素的高度和宽度?
【发布时间】:2021-10-06 03:32:00
【问题描述】:

我有一个动态组件,我在其中将子项作为道具传递。 所以道具看起来像:

interface Props {
   ...some props
   children: React.ReactNode
}

export default Layout({...some props, children}: Props) {...}

我需要在Layout 组件中访问子元素的大小(高度和宽度)。 请注意,孩子来自完全不同的组件并且是不相关的

我可以按如下方式使用 Layout 组件:

<Layout ...some props>
  <Child1 /> // I need to know the height and width of this child
  <Child2 /> // as well as this child
  <Child3 /> // and this child.
</Layout>

我怎样才能动态地做到这一点?我是否必须以某种方式将ReactNode 转换为HTMLDivElement?请注意,我无法将refs 的数组作为道具传递给Layout。因为使用Layout的页面是动态生成的

因为很多人并不真正理解我所说的动态生成是什么意思。这意味着使用 Layout 组件的页面可以传入 x 个子级。孩子的数量是未知的,但永远不会是0。

【问题讨论】:

  • 您可以使用 ref 转发,这样您就不必将 refs 作为 props 传递,并且可以直接从您的自定义 组件访问 ref。
  • @KartikMalik 我不太明白。我想访问 Layout 组件的高度和宽度,而不是 Child 组件。是否有代码示例可以进一步说明您的意思?
  • 你能展示一下页面是如何生成的吗?您是选择如何显示页面内容的人,这并不难,但我们需要更多代码来帮助您。您是否只是将{children} 添加到您的布局组件中?

标签: javascript reactjs web next.js


【解决方案1】:

您可以通过使用React.Children 在渲染子项之前动态构建引用列表来实现此目的。如果您有权访问子元素引用,则可以遵循以下方法。如果你不这样做,那么你可以按照底部的位。

您可以访问子元素引用

如果子组件传递它们的元素引用,您可以使用React.Children 循环遍历每个子组件并获取每个元素引用。然后在渲染子组件之前使用它来执行计算。

即这是一个关于如何检索和使用引用的非常简单的示例。

interface LayoutWrapperProps {
  onMount: () => void;
}

const LayoutWrapper: React.FC<LayoutWrapperProps> = ({ onMount, children }) => {
  React.useEffect(() => {
    onMount();
  }, [onMount]);

  return <>{children}</>;
};

const Layout: React.FC = ({ children }) => {
  const references = React.useRef<HTMLElement[]>([]);

  React.useEffect(() => {
    references.current = [];
  });

  function getReference(ref: HTMLElement) {
    references.current = references.current.filter(Boolean).concat(ref);
  }

  function getHeights() {
    const heights = references.current.map((ref) =>
      ref?.getBoundingClientRect()
    );
    console.log(heights);
  }

  const clonedChildren = React.Children.map(children, (child) => {
    return React.cloneElement(child as any, {
      ref: getReference
    });
  });

  return <LayoutWrapper onMount={getHeights}>{clonedChildren}</LayoutWrapper>;
};

如果您无权访问子元素引用

如果子组件没有将元素作为引用传递,则必须将动态子组件包装在组件中,以便我们可以获得元素引用。即

const WrappedComponent = React.forwardRef((props, ref) => {
   return (
     <div ref={ref}>
       {props.children}
     </div>
   )
});

在渲染子组件时,上面获取引用的代码将起作用:

<Layout>
  <WrappedComponent>
    <Child1 />
  </WrappedComponent>
</Layout>

【讨论】:

  • 谢谢。方法 1 是我正在寻找的。我正在使用打字稿,所以在这种情况下,ref 的数组应该是什么类型?
  • 使用的类型是React.RefObject
  • 来自 ReactNode 的 cloneElement 似乎不起作用
  • 感谢您的跟进。尝试执行 React.cloneElement(...) 时出现一些奇怪的错误。我得到的错误是“类型‘未定义’不可分配给类型‘ReactElement ReactElement | null) | (new (props: any) => Component )>'。”这里的例子codesandbox.io/s/intelligent-spence-qvsej?file=/src/…
  • 这个怎么样? codesandbox.io/s/crazy-fermi-9z5ys?file=/src/App.tsx,这应该会获取挂载的元素以及后续更新
【解决方案2】:

由于我们不知道您的children 是如何构建的,因此我可以向您建议:

import React from 'react';
import { render } from 'react-dom';

const App = () => {
  const el1Ref = React.useRef();
  const el2Ref = React.useRef();

  const [childrenValues, setChildrenValues] = React.useState([]);

  React.useEffect(() => {
    setChildrenValues([
      el1Ref.current.getBoundingClientRect(),
      el2Ref.current.getBoundingClientRect()
    ]);
  }, []);
  return (
    <Parent childrenVals={childrenValues}>
      <span ref={el1Ref}>
        <Child value="Hello" />
      </span>
      <span ref={el2Ref}>
        <Child value="<div>Hello<br />World</div>" />
      </span>
    </Parent>
  );
};

const Parent = ({ children, childrenVals }) => {
  React.useEffect(() => {
    console.log('children values from parent = ', childrenVals);
  });
  return <>{children}</>;
};

const Child = ({ value }) => {
  return <div dangerouslySetInnerHTML={{ __html: value }} />;
};

render(<App />, document.getElementById('root'));

这里是repro on Stackblitz

这个想法是操纵你的孩子是如何建造的。

【讨论】:

  • 孩子是动态设置的,所以这行不通
猜你喜欢
  • 2019-12-29
  • 2019-04-11
  • 1970-01-01
  • 2013-08-11
  • 1970-01-01
  • 2020-03-03
  • 2019-12-26
  • 1970-01-01
  • 2015-09-21
相关资源
最近更新 更多