Lightweight and flexible React hook to animate height change
Hiding or showing a element with React is as simple as an if statement deciding whether to print it or not. However often you want to be able to animate the transition, so that it smoothly closes or opens. To avoid cluttering the various components with this animation logic, extract the logic into a hook:
/**
* Returns the variables needed to animate a open and close functionality via height
* @param {Boolean} isOpen: boolean is the element initially open
* @param {Number} duration: number duration in ms
*/
function useHeightAnimation(isOpen = false, duration = 350) {
const contentRef = React.useRef(); // Inner wrapper that keeps the height if the outer animated is closed
const [height, setHeight] = React.useState(isOpen ? 'auto' : 0);
const handleClick = () => {
const contentRect = contentRef.current.getBoundingClientRect();
// Set the height so the element has an initial height to transition from
setHeight(`${contentRect.height}px`);
// Set the new desired height, either 0px or full height from the innerRef
setTimeout(() => setHeight(height === 0 ? `${contentRect.height}px` : 0));
setTimeout(
() => {
if (height === 0) {
// Set the height to "auto" to prevent issues with resizing when fully opened
setHeight('auto');
}
},
duration,
height,
);
};
return { height, handleClick, contentRef };
}
The hook can be used like:
function MyComp(props) {
const duration = 350;
const { height, handleClick, contentRef } = useHeightAnimation(false, duration);
return (
<div>
<button onClick={handleClick}>Open or close</button>
<div
style={{
height: height,
overflow: height === 'auto' ? 'visible' : 'hidden',
transition: `height ${duration}ms cubic-bezier(0.4, 0, 1, 1)`,
}}
>
<div ref={contentRef}>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore
et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
cillum dolore eu fugiat nulla pariatur.
</div>
</div>
</div>
);
}