I\'d like to animate between two components where the first component fades out and is removed from the DOM before the next component is added to the DOM and fades in. Other
If you want to delay the rendering of the next component, you could use something like this:
import React, { Component } from 'react';
export default class DelayedRender extends Component {
static propTypes = {
delay: React.PropTypes.number.isRequired,
children: React.PropTypes.element,
className: React.PropTypes.string
};
constructor(props) {
super(props);
this.state = {
render: false
};
}
componentDidMount() {
setTimeout(() => {
const delayedClassNames = this.refs.noDelayed.className;
this.setState({
render: true,
classNames: delayedClassNames
});
}, this.props.delay);
}
render() {
const { children, className } = this.props;
return this.state.render ?
<div className={this.state.classNames}>{children}</div> :
<div className={className} ref="noDelayed" ></div>;
}
}
And in your render method:
const ROUTE_TRANSITION_TIME = 500;
const views = [];
if (shouldRenderDelayedRoute) {
views.push(
<DelayedRender delay={ROUTE_TRANSITION_TIME} key="book">
<A ref="book"/>
</DelayedRender>
);
} else {
views.push(<B key="library"/>);
}
<ReactCSSTransitionGroup
transitionEnterTimeout={ROUTE_TRANSITION_TIME}
transitionLeaveTimeout={ROUTE_TRANSITION_TIME}
transitionName="fade-transition"
>
{views}
</ReactCSSTransitionGroup>
Solved using the componentWillUnmount()
lifecycle method.
http://jsfiddle.net/phepyezx/9/
Here's the code:
var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup;
const Off = React.createClass({
componentWillUnmount () {
this.props.handleTransitionEnd();
},
render() {
return (
<div className="off button">OFF</div>
)
}
});
const On = React.createClass({
componentWillUnmount () {
this.props.handleTransitionEnd();
},
render() {
return (
<div className="on button">ON</div>
)
}
});
var Switch = React.createClass({
getInitialState: function() {
return {
on: false,
transitionEnd: true
};
},
toggle: function(e) {
this.setState({
on: !this.state.on,
transitionEnd: false
});
},
handleTransitionEnd() {
this.setState({transitionEnd: true});
},
renderOff() {
if (! this.state.on && this.state.transitionEnd) {
return (
<Off key="off" handleTransitionEnd={this.handleTransitionEnd} />
)
}
},
renderOn() {
if (this.state.on && this.state.transitionEnd) {
return (
<On key="on" handleTransitionEnd={this.handleTransitionEnd} />
)
}
},
render: function() {
return (
<div>
<button onClick={this.toggle}>Toggle</button>
<ReactCSSTransitionGroup transitionName="switch">
{this.renderOff()}
{this.renderOn()}
</ReactCSSTransitionGroup>
</div>
);
}
});
React.render(<Switch/>, document.getElementById("switch"));
And the relevant css:
.switch-enter {
opacity: 0.01;
}
.switch-enter.switch-enter-active {
opacity: 1.0;
transition: opacity 500ms ease-in;
}
.switch-leave {
opacity: 1.0;
}
.switch-leave.switch-leave-active {
opacity: 0;
transition: opacity 500ms ease-out;
}
You can achieve the same effective result with Jonny Buchanan's answer which uses absolute positioning and a delay instead of componentWillUnmount()
Another solution is to make the incoming and outgoing elements take up the same space, for example by having them both absolutely positioned:
<ReactCSSTransitionGroup
className="container"
component="div"
transitionName="switch">
...
.container {
position: relative;
}
.container > div {
position: absolute;
}
http://jsfiddle.net/phepyezx/7/
You can use transition-delay
to wait until the leaving component disappears before making the entering component appear, e.g.:
.fade-enter {
opacity: 0.01;
}
.fade-enter.fade-enter-active {
opacity: 1;
transition: opacity 1s;
transition-delay: 1s;
}
.fade-leave {
opacity: 1;
}
.fade-leave.fade-leave-active {
opacity: 0.01;
transition: opacity 1s;
}