@samitouri / QOS-React-2 / commits / 8760a70e73

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 });