joelmturner pyramid logo

About

Blog

Illustration

TIL

Uses

About

Blog

Illustration

TIL

Uses

React Hooks: useDims

Here's another helpful React hook. This one returns the dimensions of an element that's passed to it.

import { useState, useLayoutEffect } from 'react';
function useDims(ref, isSvg = false) {
const [dim, setDim] = useState({
height: 0,
width: 0,
top: 0,
left: 0,
});
useLayoutEffect(() => {
if (ref && ref.current) {
if (isSvg) {
const { height, width, x, y } = ref.current.getBBox();
setDim({
height,
width,
top: y,
left: x,
});
} else {
setDim({
height: ref.current.offsetHeight,
width: ref.current.offsetWidth,
top: ref.current.offsetTop,
left: ref.current.offsetLeft,
});
}
}
}, [ref, isSvg]);
return dim;
}
export default useDims;

It can be used in a component like this.

import React from 'react';
function Chart() {
const chartRef = React.useRef(null);
const { width, height, top, left } = useDims(chartRef);
return (
<div ref={chartRef}>
<ul>
<li>width: {width}</li>
<li>height: {height}</li>
<li>top: {top}</li>
<li>left: {left}</li>
</ul>
</div>
);
}

Let's break it down a bit. We start by receiving a ref and an optional boolean of isSvg. Since svgs use a different function to calculate size we can use this flag to differentiate.

Our hook is using a default dimension object in state with all values set to zero. We then return that dim if there aren't any changes.

function useDims(ref, isSvg = false) {
const [dim, setDim] = useState({
height: 0,
width: 0,
top: 0,
left: 0
});
{...}
return dim;

Next, we have useEffect which does the heavy lifting. We're checking to see if we have a value in the ref and we are having useEffect watch the ref and isSvg.

useLayoutEffect(() => {
if (ref && ref.current) {
{...}
}
}, [ref, isSvg]);

Then, we have the calcs for the html and svg element. For the html element we can pull the offsets right off of the node. The svg, however, need to pass through getBBox() to get their dimensions.

if (isSvg) {
const { height, width, x, y } = ref.current.getBBox();
setDim({
height,
width,
top: y,
left: x,
});
} else {
setDim({
height: ref.current.offsetHeight,
width: ref.current.offsetWidth,
top: ref.current.offsetTop,
left: ref.current.offsetLeft,
});
}

Once it's all together we have a quick way to grab the dimensions from elements. You can also use it for multiple elements in the same component.


Discuss this article on Twitter

Category:

dev

© 2012-2023, built with Next.js and Chakra UI

InstagramtwitterlinkedIngithubjoelmturner's DEV Profilejoelmturner's Mastodon Profile