Search code examples
javascriptreactjsreact-props

How to get the dimensions of an element passed as a prop


Let's say I have a component that takes an element as a prop called customHeader:

const Example = ({ children, customHeader }) => {
  ...some code  

  return (
    <>
      {customHeader ? customHeader : <div>Default Header</div>}
      {children}
    </>
  );
}

Then where I use the Example component I do the following:

<Example customHeader={<div>blah</div>}>
  <div>children</div>
</Example>

So far this is fairly standard stuff and everything is working but the problem I have is that I want to be able to grab the dimensions of the customHeader element by doing something like customHeader.clientHeight but that won't work. When I console.log it I get this object printed out:

{
  $$typeof: Symbol(react.element)
  key: ".0"
  props: {children: 'Blah'}
  ref: null
  type: "div"
  _owner: FiberNode {tag: 0, key: null, stateNode: null, elementType: ƒ, type: ƒ, …}
  _store: {validated: false}
}

Is there a way to convert a JSX element that is passed as a prop to an "normal" HTML element so I can read a bunch of information off it?


Solution

  • You could use cloneElement to assign an useRef to the component that will have the aRef.current.clientHeight you're looking for

    I've added a simple <button> to add some padding to the customHeader so you can see the clientHeight change it's value

    const { useEffect, useRef, useState } = React;
    
    const Example = ({ children, customHeader }) => {
       
        const aRef = useRef();
        
        useEffect(() => {
            if (aRef.current) {
                const clientHeight = aRef.current.clientHeight;
                console.log(`ClientHeight: ${clientHeight}`);
            }
        }, [ customHeader ]);
    
        return (
            <React.Fragment>
                {children}
                {
                    (customHeader) 
                        ? React.cloneElement(customHeader, { ref: aRef }) 
                        : <div>Default Header</div>
                }
            </React.Fragment>
        );
    }
    
    const App = () => {
    
        /* Demo purpose only */
        const [customHeaderPadding, setCustomHeaderPadding] = useState(0);
        const toggleCustomHeaderPadding = () => {
            const newValue = (customHeaderPadding === 0) ? 50 : 0;
            setCustomHeaderPadding(newValue);
        }
        /* Demo purpose only */
          
        return (
            <Example customHeader={<div style={{ padding: customHeaderPadding }}>blah</div>}>
               <button onClick={toggleCustomHeaderPadding}>{'Toggle Padding'}</button>
               <div>children</div>
            </Example>   
        );
    }
    
    ReactDOM.render(<App />, document.getElementById("react"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
    <div id="react"></div>