diff --git a/src/addons/transitions/ReactTransitionGroup.js b/src/addons/transitions/ReactTransitionGroup.js
index bb99b66284..501d518b1a 100644
--- a/src/addons/transitions/ReactTransitionGroup.js
+++ b/src/addons/transitions/ReactTransitionGroup.js
@@ -89,6 +89,8 @@ var ReactTransitionGroup = React.createClass({
},
componentDidUpdate: function() {
+ this.updatedChildren = null;
+
var keysToEnter = this.keysToEnter;
this.keysToEnter = [];
keysToEnter.forEach(this.performEnter);
@@ -193,9 +195,13 @@ var ReactTransitionGroup = React.createClass({
// This entered again before it fully left. Add it again.
this.performEnter(key);
} else {
- var newChildren = assign({}, this.state.children);
- delete newChildren[key];
- this.setState({children: newChildren});
+ // As this.state.children will not be updated until next render, we keep
+ // this.updatedChildren state to avoid losing all but the last removal.
+ // It's cleaned after this.state is updated, in componentDidUpdate.
+ if (!this.updatedChildren)
+ this.updatedChildren = assign({}, this.state.children);
+ delete this.updatedChildren[key];
+ this.setState({children: this.updatedChildren});
}
},
diff --git a/src/addons/transitions/__tests__/ReactTransitionGroup-test.js b/src/addons/transitions/__tests__/ReactTransitionGroup-test.js
index c1f36323d4..2e315d2818 100644
--- a/src/addons/transitions/__tests__/ReactTransitionGroup-test.js
+++ b/src/addons/transitions/__tests__/ReactTransitionGroup-test.js
@@ -208,4 +208,65 @@ describe('ReactTransitionGroup', function() {
'didMount', 'didMount', 'willEnter', 'didEnter'
]);
});
+
+ it('should handle entering/leaving several elements at once', function() {
+ var log = [];
+ var cb;
+
+ var Child = React.createClass({
+ componentDidMount: function() {
+ log.push('didMount'+this.props.id);
+ },
+ componentWillEnter: function(cb) {
+ log.push('willEnter'+this.props.id);
+ cb();
+ },
+ componentDidEnter: function() {
+ log.push('didEnter'+this.props.id);
+ },
+ componentWillLeave: function(cb) {
+ log.push('willLeave'+this.props.id);
+ cb();
+ },
+ componentDidLeave: function() {
+ log.push('didLeave'+this.props.id);
+ },
+ componentWillUnmount: function() {
+ log.push('willUnmount'+this.props.id);
+ },
+ render: function() {
+ return ;
+ }
+ });
+
+ var Component = React.createClass({
+ getInitialState: function() {
+ return {count: 1};
+ },
+ render: function() {
+ var children = [];
+ for (var i = 0; i < this.state.count; i++) {
+ children.push();
+ }
+ return {children};
+ }
+ });
+
+ var instance = React.render(, container);
+ expect(log).toEqual(['didMount0']);
+ log = [];
+
+ instance.setState({count: 3});
+ expect(log).toEqual([
+ 'didMount1', 'didMount2', 'willEnter1', 'didEnter1',
+ 'willEnter2', 'didEnter2'
+ ]);
+ log = [];
+
+ instance.setState({count: 0});
+ expect(log).toEqual([
+ 'willLeave0', 'didLeave0', 'willLeave1', 'didLeave1',
+ 'willLeave2', 'didLeave2', 'willUnmount0', 'willUnmount1', 'willUnmount2'
+ ]);
+ });
});