Adding "appear" phase to ReactTransitionGroup and ReactCSSTransitionGroup.

"appear" differs from "enter" in that all children of a transition group at mount time will "appear" but will not "enter". All children later added to an existing transition group will "enter" but not "appear".

This extra transition phase allows for animation-on-mount effects.

A mirroring "appear" prop has been added to ReactCSSTransitionGroup, however for reverse-compatibility (and because "appear" is less common) it defaults to false.

Thanks to @afa for his work investigating the possible ways to implement this.
This commit is contained in:
Lee Byron
2014-11-12 21:27:11 -05:00
parent 06bf89db8e
commit 8760a70e73
4 changed files with 71 additions and 12 deletions
@@ -28,12 +28,14 @@ var ReactCSSTransitionGroup = React.createClass({
propTypes: {
transitionName: React.PropTypes.string.isRequired,
transitionAppear: React.PropTypes.bool,
transitionEnter: React.PropTypes.bool,
transitionLeave: React.PropTypes.bool
},
getDefaultProps: function() {
return {
transitionAppear: false,
transitionEnter: true,
transitionLeave: true
};
@@ -46,6 +48,7 @@ var ReactCSSTransitionGroup = React.createClass({
return ReactCSSTransitionGroupChild(
{
name: this.props.transitionName,
appear: this.props.transitionAppear,
enter: this.props.transitionEnter,
leave: this.props.transitionLeave
},
@@ -107,6 +107,14 @@ var ReactCSSTransitionGroupChild = React.createClass({
}
},
componentWillAppear: function(done) {
if (this.props.appear) {
this.transition('appear', done);
} else {
done();
}
},
componentWillEnter: function(done) {
if (this.props.enter) {
this.transition('enter', done);
+47 -6
View File
@@ -39,6 +39,21 @@ var ReactTransitionGroup = React.createClass({
};
},
componentWillMount: function() {
this.currentlyTransitioningKeys = {};
this.keysToEnter = [];
this.keysToLeave = [];
},
componentDidMount: function() {
var initialChildMapping = this.state.children;
for (var key in initialChildMapping) {
if (initialChildMapping[key]) {
this.performAppear(key);
}
}
},
componentWillReceiveProps: function(nextProps) {
var nextChildMapping = ReactTransitionChildMapping.getChildMapping(
nextProps.children
@@ -73,12 +88,6 @@ var ReactTransitionGroup = React.createClass({
// If we want to someday check for reordering, we could do it here.
},
componentWillMount: function() {
this.currentlyTransitioningKeys = {};
this.keysToEnter = [];
this.keysToLeave = [];
},
componentDidUpdate: function() {
var keysToEnter = this.keysToEnter;
this.keysToEnter = [];
@@ -89,6 +98,38 @@ var ReactTransitionGroup = React.createClass({
keysToLeave.forEach(this.performLeave);
},
performAppear: function(key) {
this.currentlyTransitioningKeys[key] = true;
var component = this.refs[key];
if (component.componentWillAppear) {
component.componentWillAppear(
this._handleDoneAppearing.bind(this, key)
);
} else {
this._handleDoneAppearing(key);
}
},
_handleDoneAppearing: function(key) {
var component = this.refs[key];
if (component.componentDidAppear) {
component.componentDidAppear();
}
delete this.currentlyTransitioningKeys[key];
var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
this.props.children
);
if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
// This was removed before it had fully appeared. Remove it.
this.performLeave(key);
}
},
performEnter: function(key) {
this.currentlyTransitioningKeys[key] = true;
@@ -36,6 +36,13 @@ describe('ReactTransitionGroup', function() {
componentDidMount: function() {
log.push('didMount');
},
componentWillAppear: function(cb) {
log.push('willAppear');
cb();
},
componentDidAppear: function() {
log.push('didAppear');
},
componentWillEnter: function(cb) {
log.push('willEnter');
cb();
@@ -72,15 +79,15 @@ describe('ReactTransitionGroup', function() {
});
var instance = React.render(<Component />, container);
expect(log).toEqual(['didMount']);
expect(log).toEqual(['didMount', 'willAppear', 'didAppear']);
log = [];
instance.setState({count: 2}, function() {
expect(log).toEqual(['didMount', 'didMount', 'willEnter', 'didEnter']);
expect(log).toEqual(['didMount', 'willEnter', 'didEnter']);
log = [];
instance.setState({count: 1}, function() {
expect(log).toEqual([
"didMount", "didMount", "willEnter", "didEnter",
"willLeave", "didLeave", "willUnmount"
]);
expect(log).toEqual(['willLeave', 'didLeave', 'willUnmount']);
});
});
});