Skip to content

Commit e57c0ed

Browse files
committed
Support no children so the child component can be removed / added.
1 parent 681cc3b commit e57c0ed

File tree

1 file changed

+63
-39
lines changed

1 file changed

+63
-39
lines changed

src/ReactCSSTransitionReplace.jsx

Lines changed: 63 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import ReactCSSTransitionGroupChild from 'react/lib/ReactCSSTransitionGroupChild
1212

1313
const reactCSSTransitionGroupChild = React.createFactory(ReactCSSTransitionGroupChild);
1414

15+
const TICK = 17;
16+
17+
1518
function createTransitionTimeoutPropValidator(transitionType) {
1619
const timeoutPropName = 'transition' + transitionType + 'Timeout';
1720
const enabledPropName = 'transition' + transitionType;
@@ -57,8 +60,9 @@ export default class ReactCSSTransitionReplace extends React.Component {
5760
};
5861

5962
state = {
60-
currentChild: React.Children.only(this.props.children),
61-
nextChild: null
63+
currentChild: this.props.children ? React.Children.only(this.props.children) : null,
64+
nextChild: null,
65+
height: null
6266
};
6367

6468
componentDidMount() {
@@ -67,8 +71,15 @@ export default class ReactCSSTransitionReplace extends React.Component {
6771
}
6872
}
6973

74+
componentWillUnmount() {
75+
if (this.timeout) {
76+
clearTimeout(this.timeout);
77+
}
78+
}
79+
7080
componentWillReceiveProps(nextProps) {
71-
const nextChild = nextProps.children ? React.Children.only(nextProps.children) : null;
81+
// Setting false indicates that the child has changed, but it is a removal so there is no next child.
82+
const nextChild = nextProps.children ? React.Children.only(nextProps.children) : false;
7283
const currentChild = this.state.currentChild;
7384

7485
if (currentChild && nextChild && nextChild.key === currentChild.key) {
@@ -78,38 +89,30 @@ export default class ReactCSSTransitionReplace extends React.Component {
7889
});
7990
}
8091

81-
// Set the next child to start the transition,
82-
// also set the current and next height to trigger its transition.
92+
// Set the next child to start the transition, and set the current height.
8393
this.setState({
8494
nextChild,
85-
height: ReactDOM.findDOMNode(this.refs.curr).offsetHeight
86-
}, () => this.setState({
87-
height: ReactDOM.findDOMNode(this.refs.next).offsetHeight
88-
}));
95+
height: this.state.currentChild ? ReactDOM.findDOMNode(this.refs.curr).offsetHeight : 0
96+
});
97+
98+
// Enqueue setting the next height to trigger the height transition.
99+
this.timeout = setTimeout(() => {
100+
this.setState({height: this.state.nextChild ? ReactDOM.findDOMNode(this.refs.next).offsetHeight : 0});
101+
this.timeout = null;
102+
}, TICK);
89103
}
90104

91105
componentDidUpdate() {
92-
if (this.state.nextChild && !this.isTransitioning) {
93-
this.enterNext();
94-
this.leaveCurrent();
106+
if (!this.isTransitioning) {
107+
if (this.state.nextChild) {
108+
this.enterNext();
109+
}
110+
if (this.state.currentChild && (this.state.nextChild || this.state.nextChild === false)) {
111+
this.leaveCurrent();
112+
}
95113
}
96114
}
97115

98-
_wrapChild(child, moreProps) {
99-
// We need to provide this childFactory so that
100-
// ReactCSSTransitionReplaceChild can receive updates to name,
101-
// enter, and leave while it is leaving.
102-
return reactCSSTransitionGroupChild(objectAssign({
103-
name: this.props.transitionName,
104-
appear: this.props.transitionAppear,
105-
enter: this.props.transitionEnter,
106-
leave: this.props.transitionLeave,
107-
appearTimeout: this.props.transitionAppearTimeout,
108-
enterTimeout: this.props.transitionEnterTimeout,
109-
leaveTimeout: this.props.transitionLeaveTimeout
110-
}, moreProps), child);
111-
}
112-
113116
appearCurrent() {
114117
this.refs.curr.componentWillAppear(this._handleDoneAppearing);
115118
this.isTransitioning = true;
@@ -135,18 +138,39 @@ export default class ReactCSSTransitionReplace extends React.Component {
135138

136139
leaveCurrent() {
137140
this.refs.curr.componentWillLeave(this._handleDoneLeaving);
141+
this.isTransitioning = true;
138142
}
139143

140-
// When the leave transition timeOut expires the animation classes are removed, so the
144+
// When the leave transition time-out expires the animation classes are removed, so the
141145
// element must be removed from the DOM if the enter transition is still in progress.
142146
_handleDoneLeaving = () => {
143147
if (this.isTransitioning) {
144-
this.setState({
145-
currentChild: null
146-
});
148+
const state = {currentChild: null};
149+
150+
if (!this.state.nextChild) {
151+
this.isTransitioning = false;
152+
state.height = null;
153+
}
154+
155+
this.setState(state);
147156
}
148157
};
149158

159+
_wrapChild(child, moreProps) {
160+
// We need to provide this childFactory so that
161+
// ReactCSSTransitionReplaceChild can receive updates to name,
162+
// enter, and leave while it is leaving.
163+
return reactCSSTransitionGroupChild(objectAssign({
164+
name: this.props.transitionName,
165+
appear: this.props.transitionAppear,
166+
enter: this.props.transitionEnter,
167+
leave: this.props.transitionLeave,
168+
appearTimeout: this.props.transitionAppearTimeout,
169+
enterTimeout: this.props.transitionEnterTimeout,
170+
leaveTimeout: this.props.transitionLeaveTimeout
171+
}, moreProps), child);
172+
}
173+
150174
render() {
151175
const { currentChild, nextChild, height } = this.state;
152176
const childrenToRender = [];
@@ -159,20 +183,20 @@ export default class ReactCSSTransitionReplace extends React.Component {
159183
}));
160184
}
161185

162-
if (nextChild) {
163-
objectAssign(containerProps, {
164-
className: `${containerProps.className || ''} ${containerProps.transitionName}-height`,
165-
style: objectAssign({}, containerProps.style, {
166-
position: 'relative',
167-
display: 'block',
168-
height
169-
})
186+
if (height !== null) {
187+
containerProps.className = `${containerProps.className || ''} ${containerProps.transitionName}-height`;
188+
containerProps.style = objectAssign({}, containerProps.style, {
189+
position: 'relative',
190+
display: 'block',
191+
height
170192
});
171193

172194
if (overflowHidden) {
173195
containerProps.style.overflow = 'hidden';
174196
}
197+
}
175198

199+
if (nextChild) {
176200
childrenToRender.push(
177201
React.createElement('span',
178202
{

0 commit comments

Comments
 (0)