tester778899
tester778899

Reputation: 69

Get total width of JSX Element

How could I get the width of a JSX.Element? If I was in vanilla, I would do something like

const button = document.createElement('button');
document.body.appendChild(button)
window.getComputedStyle(button).width

Now I need to do the same, but it seems ref is null and I'm not sure even how to temporarily append to the DOM just to see what its width would be.

const button: JSX.Element = <CustomButton/>;
/// ....

Upvotes: 0

Views: 387

Answers (2)

edemaine
edemaine

Reputation: 3120

The canonical way is to use a ref, and then observe it within an effect, which is called after the DOM gets rendered:

const ref = useRef();
useEffect(() => {
  console.log(window.getComputedStyle(ref.current).width)
}, []);
return <button ref={ref}/>;

Upvotes: 0

Claudio
Claudio

Reputation: 134

Couple of things to check and consider.

  1. where is the ref created and does it get forwarded correctly to a valid dom element (make sure that CustomButton uses forwardRef).
  2. you don't need to append anything in react to look at the width. all you need is ref.current.clientWidth or ref.current.getBoundingClientRect(), but ref.current has to exist in the first place :-)
  3. if you need access to the ref.current element when your component first mounts (and not in a onClick or some other callback - then this does not apply) you'll have to use useLayoutEffect as the javascript runs before the dom is rendered so there is technically no to measure yet.

See this example:

ParentComponent.tsx


import {useState, useLayoutEffect} from 'react';

const ParentComponent = () => {
    const [width, setWidth] = useState<number | null>(null);

    useLayoutEffect(() => {
        if(ref?.current && !width) {
            const { clientWidth } = ref.current;
            setWidth(clientWidth);
        }
    }, [ref?.current]);
    
   console.log('width', width);

// `width` will be null at first render,
// then when CustomButton renders and <button> is created the ref will be 
// updated, triggering your layout side effect that saves the 
// clientWidth to the state. State change will trigger a rerender of 
// ParentComponent and your console.log will finally print the width (whose
// value is stored in the state).

    return <CustomButton ref={ref}/>;
};

CustomButton.tsx


import {forwardRef} from 'react';

const CustomButton = forwarRef((props, ref) => {

    return (
        <>
        // some other stuff
        <button ref={ref}/>>Click</button>
        </>
     );
};

Upvotes: 1

Related Questions