Joel M. Turner

Illustration

Blog

Notes

React Hooks: useDims

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

1import { useState, useLayoutEffect } from "react";
2
3function useDims(ref, isSvg = false) {
4 const [dim, setDim] = useState({
5 height: 0,
6 width: 0,
7 top: 0,
8 left: 0
9 });
10
11 useLayoutEffect(() => {
12 if (ref && ref.current) {
13 if (isSvg) {
14 const {height, width, x, y} = ref.current.getBBox();
15 setDim({
16 height,
17 width,
18 top: y,
19 left: x
20 });
21 } else {
22 setDim({
23 height: ref.current.offsetHeight,
24 width: ref.current.offsetWidth,
25 top: ref.current.offsetTop,
26 left: ref.current.offsetLeft
27 });
28 }
29 }
30 }, [ref, isSvg]);
31
32 return dim;
33};
34
35export default useDims;

It can be used in a component like this.

1import React from 'react';
2
3function Chart() {
4 const chartRef = React.useRef(null);
5 const {width, height, top, left} = useDims(chartRef);
6
7 return (
8 <div ref={chartRef}>
9 <ul>
10 <li>width: {width}</li>
11 <li>height: {height}</li>
12 <li>top: {top}</li>
13 <li>left: {left}</li>
14 </ul>
15 </div>
16 )
17}

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.

1function useDims(ref, isSvg = false) {
2 const [dim, setDim] = useState({
3 height: 0,
4 width: 0,
5 top: 0,
6 left: 0
7 });
8
9 {...}
10
11 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.

1useLayoutEffect(() => {
2 if (ref && ref.current) {
3 {...}
4 }
5}, [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.

1if (isSvg) {
2 const {height, width, x, y} = ref.current.getBBox();
3 setDim({
4 height,
5 width,
6 top: y,
7 left: x
8 });
9} else {
10 setDim({
11 height: ref.current.offsetHeight,
12 width: ref.current.offsetWidth,
13 top: ref.current.offsetTop,
14 left: ref.current.offsetLeft
15 });
16}

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.

<-- My Favorite Layout Components in React

Discuss this article on Twitter