I want to calculate the height of a component and send it to its parent when the page is loaded and resized.
I'm using the below reusable Hook to successfully measure the height of the div inside the header component. But how do I send the height
calculated from useDimensions
in the child to its parent component as headHeight
?
Measuring Hook
import { useState, useCallback, useEffect } from 'react';
function getDimensionObject(node) {
const rect = node.getBoundingClientRect();
return {
width: rect.width,
height: rect.height,
top: 'x' in rect ? rect.x : rect.top,
left: 'y' in rect ? rect.y : rect.left,
x: 'x' in rect ? rect.x : rect.left,
y: 'y' in rect ? rect.y : rect.top,
right: rect.right,
bottom: rect.bottom
};
}
export function useDimensions(data = null, liveMeasure = true) {
const [dimensions, setDimensions] = useState({});
const [node, setNode] = useState(null);
const ref = useCallback(node => {
setNode(node);
}, []);
useEffect(() => {
if (node) {
const measure = () =>
window.requestAnimationFrame(() =>
setDimensions(getDimensionObject(node))
);
measure();
if (liveMeasure) {
window.addEventListener('resize', measure);
window.addEventListener('scroll', measure);
return () => {
window.removeEventListener('resize', measure);
window.removeEventListener('scroll', measure);
};
}
}
}, [node, data]);
return [ref, dimensions, node];
}
Parent
export default function Main(props: { notifications: Notification[] }) {
const { notifications } = props;
const [headHeight, setHeadHeight] = useState(0)
const updateHeadHeight = () => {
setHeadHeight(headHeight)
}
return (
<main>
<Header updateParent={updateHeadHeight}/>
{headHeight}
</main>
)
}
Child
import { useDimensions } from '../../lib/design/measure';
import React, { useState, useLayoutEffect } from 'react';
export default function DefaultHeader(props, data) {
const [
ref,
{ height, width, top, left, x, y, right, bottom }
] = useDimensions(data);
;
return <>
<div ref={ref} className="header">
<h1>Hello!</h1>
</div>
</>
}
Personally, I would call the hook in the Main
component and wrap the child component in a forwardRef
(check docs here).
See full example here:
Main.tsx
export default function Main(props: { notifications: Notification[] }) {
const { notifications } = props;
const [ref, dimensions] = useDimensions()
return (
<main>
<Header ref={ref}/>
{JSON.stringify(dimensions)}
</main>
)
}
What's done here, we just pass the ref down the tree to the child component and we just show the dimensions (testing purposes).
DefaultHeader.tsx
import { forwardRef } from "react";
const DefaultHeader = forwardRef((_, ref) => {
return (
<>
<div ref={ref} className="header" >
<h1>Hello!</h1>
</div>
</>
);
});
export default DefaultHeader;
Here, we just attach the ref to the container that it has previously been (your example).
See full example on this CodeSandbox.
Let me know if you need more explanations on this.