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.
Lee Byron committed
Nov 12, 2014 at 21:27 UTC
8760a70e735cd7afcf70108c133d7df81fda3ef3
4 files changed
+71
-12
src/addons/transitions/ReactCSSTransitionGroup.js
+3
@@ -28,12 +28,14 @@ var ReactCSSTransitionGroup = React.createClass({
28
29
propTypes: {
30
transitionName: React.PropTypes.string.isRequired,
31
+ transitionAppear: React.PropTypes.bool,
32
transitionEnter: React.PropTypes.bool,
33
transitionLeave: React.PropTypes.bool
34
},
35
36
getDefaultProps: function() {
37
return {
38
+ transitionAppear: false,
39
transitionEnter: true,
40
transitionLeave: true
41
};
@@ -46,6 +48,7 @@ var ReactCSSTransitionGroup = React.createClass({
48
return ReactCSSTransitionGroupChild(
49
{
50
name: this.props.transitionName,
51
+ appear: this.props.transitionAppear,
52
enter: this.props.transitionEnter,
53
leave: this.props.transitionLeave
54
},
src/addons/transitions/ReactCSSTransitionGroupChild.js
+8
@@ -107,6 +107,14 @@ var ReactCSSTransitionGroupChild = React.createClass({
107
}
108
},
109
110
+ componentWillAppear: function(done) {
111
+ if (this.props.appear) {
112
+ this.transition('appear', done);
113
+ } else {
114
+ done();
115
+ }
116
+ },
117
+
118
componentWillEnter: function(done) {
119
if (this.props.enter) {
120
this.transition('enter', done);
src/addons/transitions/ReactTransitionGroup.js
+47
-6
@@ -39,6 +39,21 @@ var ReactTransitionGroup = React.createClass({
39
};
40
},
41
42
+ componentWillMount: function() {
43
+ this.currentlyTransitioningKeys = {};
44
+ this.keysToEnter = [];
45
+ this.keysToLeave = [];
46
+ },
47
+
48
+ componentDidMount: function() {
49
+ var initialChildMapping = this.state.children;
50
+ for (var key in initialChildMapping) {
51
+ if (initialChildMapping[key]) {
52
+ this.performAppear(key);
53
+ }
54
+ }
55
+ },
56
+
57
componentWillReceiveProps: function(nextProps) {
58
var nextChildMapping = ReactTransitionChildMapping.getChildMapping(
59
nextProps.children
@@ -73,12 +88,6 @@ var ReactTransitionGroup = React.createClass({
88
// If we want to someday check for reordering, we could do it here.
89
},
90
76
- componentWillMount: function() {
77
- this.currentlyTransitioningKeys = {};
78
- this.keysToEnter = [];
79
- this.keysToLeave = [];
80
- },
81
-
91
componentDidUpdate: function() {
92
var keysToEnter = this.keysToEnter;
93
this.keysToEnter = [];
@@ -89,6 +98,38 @@ var ReactTransitionGroup = React.createClass({
98
keysToLeave.forEach(this.performLeave);
99
},
100
101
+ performAppear: function(key) {
102
+ this.currentlyTransitioningKeys[key] = true;
103
+
104
+ var component = this.refs[key];
105
+
106
+ if (component.componentWillAppear) {
107
+ component.componentWillAppear(
108
+ this._handleDoneAppearing.bind(this, key)
109
+ );
110
+ } else {
111
+ this._handleDoneAppearing(key);
112
+ }
113
+ },
114
+
115
+ _handleDoneAppearing: function(key) {
116
+ var component = this.refs[key];
117
+ if (component.componentDidAppear) {
118
+ component.componentDidAppear();
119
+ }
120
+
121
+ delete this.currentlyTransitioningKeys[key];
122
+
123
+ var currentChildMapping = ReactTransitionChildMapping.getChildMapping(
124
+ this.props.children
125
+ );
126
+
127
+ if (!currentChildMapping || !currentChildMapping.hasOwnProperty(key)) {
128
+ // This was removed before it had fully appeared. Remove it.
129
+ this.performLeave(key);
130
+ }
131
+ },
132
+
133
performEnter: function(key) {
134
this.currentlyTransitioningKeys[key] = true;
135
src/addons/transitions/__tests__/ReactTransitionGroup-test.js
+13
-6
@@ -36,6 +36,13 @@ describe('ReactTransitionGroup', function() {
36
componentDidMount: function() {
37
log.push('didMount');
38
},
39
+ componentWillAppear: function(cb) {
40
+ log.push('willAppear');
41
+ cb();
42
+ },
43
+ componentDidAppear: function() {
44
+ log.push('didAppear');
45
+ },
46
componentWillEnter: function(cb) {
47
log.push('willEnter');
48
cb();
@@ -72,15 +79,15 @@ describe('ReactTransitionGroup', function() {
79
});
80
81
var instance = React.render(<Component />, container);
75
- expect(log).toEqual(['didMount']);
82
+ expect(log).toEqual(['didMount', 'willAppear', 'didAppear']);
83
84
+ log = [];
85
instance.setState({count: 2}, function() {
78
- expect(log).toEqual(['didMount', 'didMount', 'willEnter', 'didEnter']);
86
+ expect(log).toEqual(['didMount', 'willEnter', 'didEnter']);
87
+
88
+ log = [];
89
instance.setState({count: 1}, function() {
80
- expect(log).toEqual([
81
- "didMount", "didMount", "willEnter", "didEnter",
82
- "willLeave", "didLeave", "willUnmount"
83
- ]);
90
+ expect(log).toEqual(['willLeave', 'didLeave', 'willUnmount']);
91
});
92
});
93
});