简体   繁体   English

如何从 ReactNode 获取元素的高度和宽度?

[英]How to get element height and width from ReactNode?

I have a dynamic component in which I pass in children as prop.我有一个动态组件,我在其中将孩子作为道具传递。 So the props look something like:所以道具看起来像:

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

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

I need to access the size of the children elements (height and width), in the Layout component.我需要在Layout组件中访问子元素的大小(高度和宽度)。 Note that the children are from completely different components and are non-related .请注意,孩子来自完全不同的组件并且是不相关的

I can use the Layout component as follow:我可以按如下方式使用 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>

How can I do so dynamically?我怎样才能动态地这样做? Do I somehow have to convert ReactNode to HTMLDivElement ?我是否必须以某种方式将ReactNode转换为HTMLDivElement Note that there is no way I can pass in an array of refs as a prop into Layout .请注意,我无法将refs数组作为道具传入Layout Because that the pages which use Layout are dynamically generated .因为使用Layout的页面是动态生成的

You can achieve this by using React.Children to dynamically build up a list of references before rendering the children.您可以通过使用React.Children在渲染子项之前动态构建引用列表来实现这一点。 If you have access to the children element references, you can follow the below approach.如果您有权访问子元素引用,则可以遵循以下方法。 If you don't then you can follow the bit at the bottom.如果你不这样做,那么你可以按照底部的位。

You have access to the children element references您可以访问子元素引用

If the children components pass up their element reference, you can use React.Children to loop through each child and get each element reference.如果子组件传递了它们的元素引用,您可以使用React.Children循环遍历每个子组件并获取每个元素引用。 Then use this to perform calculations before the children components are rendered.然后在渲染子组件之前使用它来执行计算。

ie This is a very simple example on how to retrieve the references and use them.即这是一个关于如何检索引用并使用它们的非常简单的示例。 This wouldn't be performant if there were multiple components added/removed as it stores it in a set and doesn't remove components.如果添加/删除了多个组件,因为它将它存储在一个集合中并且不删除组件,这将不会是高性能的。

const Layout = ({ children }) => {
  const references = useRef(new Set());

  function getReference(ref) {
    references.current.add(ref);
  }

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

  const heights = React.useMemo(() => {
    return Array.from(references.current).map((ref) => ref.getBoundingClientRect());
  }, [references.current]);

  return clonedChildren;
}

If you don't have access to the children element references如果您无权访问子元素引用

If the children components aren't passing up an element as the reference, you'll have to wrap the dynamic children components in a component so we can get an element reference.如果子组件没有传递元素作为引用,则必须将动态子组件包装在一个组件中,以便我们可以获得元素引用。 ie IE

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

When rendering the children components, then the code above that gets the references will work:渲染子组件时,上面获取引用的代码将起作用:

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

Since we don't know how your children is built, here is what I can propose you :由于我们不知道您的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'));

And here is the repro on Stackblitz .这是Stackblitz 上重现

The idea is to manipulate how your children is built.这个想法是操纵你的孩子是如何建造的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM