How to animate element height in React with ReactCSSTransitionGroup?
If you dont want to import a module or use qjuery, here is a template using React ref (https://reactjs.org/docs/refs-and-the-dom.html)
Basically you get the height it will be, grow to that height, switch back to auto. On the way back switch to the height it is, then back to 0.
class CollapsibleSectionBlock extends React.Component {
constructor(props) {
super(props);
this.state = {
showContent: false,
height: "0px",
myRef: null,
};
}
componentDidUpdate = (prevProps, prevState) => {
if (prevState.height === "auto" && this.state.height !== "auto") {
setTimeout(() => this.setState({ height: "0px" }), 1);
}
}
setInnerRef = (ref) => this.setState({ myRef: ref });
toggleOpenClose = () => this.setState({
showContent: !this.state.showContent,
height: this.state.myRef.scrollHeight,
});
updateAfterTransition = () => {
if (this.state.showContent) {
this.setState({ height: "auto" });
}
};
render() {
const { title, children } = this.props;
return (
<div>
<h2 onClick={() => this.toggleOpenClose()}>
{title}
</h2>
<div
ref={this.setInnerRef}
onTransitionEnd={() => this.updateAfterTransition()}
style={{
height: this.state.height,
overflow: "hidden",
transition: "height 250ms linear 0s",
}}
>
{children}
</div>
</div>
);
}
}
A very easy and nice approach is by handling scaleY instead of height. The hover should set the attribute transform: scaleY(1) (originally => transform: scaleY(0) to hide it) and the animation should target transform attribute.
I had a same problem and ended up writing a standalone component for animating height.
You can see the demo here: https://stanko.github.io/react-animate-height/
It is much easier to use, and whole library is really small (~200 lines)
<AnimateHeight
duration={ 500 }
height={ 'auto' }
>
<h1>Your content goes here</h1>
<p>Put as many React or HTML components here.</p>
</AnimateHeight>
Sorry for the shameless self promotion, but I think it can save you a lot of time if you have more than one component to animate.
Cheers!
After a bit more experiment, I've come up with a solution by using the low-level API ReactTransitionGroup
instead of high-level ReactCSSTransitionGroup
Here's the JSFiddle with a working solution: http://jsfiddle.net/cherrry/0wgp34cr/
Before the animation, it's doing 3 things:
- get computed height, paddings and margins
- hide the element with
display: none
and add.anim-enter
to set height to 0 - create css rule for
.anim-enter-active
To start the animation, it's doing 2 things:
- unhide the element
- add
.anim-enter-active
to start the animation
Some numbers and class name in JSFiddle were hard-coded, but it should be easy enough to transform the "mixin" into a React class as a replacement of ReactCSSTransitionGroup