Skip to content

Commit 64ae270

Browse files
committed
Maintain component callback refs by not overwriting with string refs.
1 parent ba38fe0 commit 64ae270

File tree

2 files changed

+56
-21
lines changed

2 files changed

+56
-21
lines changed

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
"prepublish": "npm run build"
1515
},
1616
"dependencies": {
17+
"chain-function": "^1.0.0",
1718
"prop-types": "^15.5.6",
18-
"react-transition-group": "^1.1.1"
19+
"react-transition-group": "^1.1.1",
20+
"warning": "^3.0.0"
1921
},
2022
"peerDependencies": {
2123
"react": "^15.0.0",

src/ReactCSSTransitionReplace.jsx

Lines changed: 53 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import React from 'react'
88
import ReactDOM from 'react-dom'
99
import PropTypes from 'prop-types'
10+
import chain from 'chain-function'
11+
import warning from 'warning'
1012

1113
import ReactCSSTransitionGroupChild from 'react-transition-group/CSSTransitionGroupChild'
1214
import { nameShape, transitionTimeout } from 'react-transition-group/utils/PropTypes'
@@ -42,11 +44,17 @@ export default class ReactCSSTransitionReplace extends React.Component {
4244
childComponent: 'span',
4345
}
4446

45-
state = {
46-
currentKey: '1',
47-
currentChild: this.props.children ? React.Children.only(this.props.children) : undefined,
48-
prevChildren: {},
49-
height: null,
47+
constructor(props, context) {
48+
super(props, context)
49+
50+
this.childRefs = Object.create(null)
51+
52+
this.state = {
53+
currentKey: '1',
54+
currentChild: this.props.children ? React.Children.only(this.props.children) : undefined,
55+
prevChildren: {},
56+
height: null,
57+
}
5058
}
5159

5260
componentWillMount() {
@@ -73,8 +81,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
7381
return
7482
}
7583

76-
const {state} = this
77-
const {currentKey} = state
84+
const {currentKey, prevChildren} = this.state
7885

7986
const nextState = {
8087
currentKey: String(Number(currentKey) + 1),
@@ -87,9 +94,9 @@ export default class ReactCSSTransitionReplace extends React.Component {
8794
}
8895

8996
if (currentChild) {
90-
nextState.height = ReactDOM.findDOMNode(this.refs[currentKey]).offsetHeight
97+
nextState.height = ReactDOM.findDOMNode(this.childRefs[currentKey]).offsetHeight
9198
nextState.prevChildren = {
92-
...state.prevChildren,
99+
...prevChildren,
93100
[currentKey]: currentChild,
94101
}
95102
if (!this.transitioningKeys[currentKey]) {
@@ -113,7 +120,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
113120

114121
performAppear(key) {
115122
this.transitioningKeys[key] = true
116-
this.refs[key].componentWillAppear(this.handleDoneAppearing.bind(this, key))
123+
this.childRefs[key].componentWillAppear(this.handleDoneAppearing.bind(this, key))
117124
}
118125

119126
handleDoneAppearing = (key) => {
@@ -126,7 +133,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
126133

127134
performEnter(key) {
128135
this.transitioningKeys[key] = true
129-
this.refs[key].componentWillEnter(this.handleDoneEntering.bind(this, key))
136+
this.childRefs[key].componentWillEnter(this.handleDoneEntering.bind(this, key))
130137
this.enqueueHeightTransition()
131138
}
132139

@@ -143,7 +150,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
143150

144151
performLeave = (key) => {
145152
this.transitioningKeys[key] = true
146-
this.refs[key].componentWillLeave(this.handleDoneLeaving.bind(this, key))
153+
this.childRefs[key].componentWillLeave(this.handleDoneLeaving.bind(this, key))
147154
if (!this.state.currentChild) {
148155
// The enter transition dominates, but if there is no
149156
// entering component the height is set to zero.
@@ -156,6 +163,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
156163

157164
const nextState = {prevChildren: {...this.state.prevChildren}}
158165
delete nextState.prevChildren[key]
166+
delete this.childRefs[key]
159167

160168
if (!this.state.currentChild) {
161169
nextState.height = null
@@ -170,14 +178,14 @@ export default class ReactCSSTransitionReplace extends React.Component {
170178
if (!state.currentChild) {
171179
return this.setState({height: 0})
172180
}
173-
this.setState({height: ReactDOM.findDOMNode(this.refs[state.currentKey]).offsetHeight})
181+
this.setState({height: ReactDOM.findDOMNode(this.childRefs[state.currentKey]).offsetHeight})
174182
}, TICK)
175183
}
176184

177185
wrapChild(child, moreProps) {
178186
let transitionName = this.props.transitionName
179187

180-
if (typeof transitionName == 'object' && transitionName !== null) {
188+
if (typeof transitionName === 'object' && transitionName !== null) {
181189
transitionName = {...transitionName}
182190
delete transitionName.height
183191
}
@@ -209,7 +217,7 @@ export default class ReactCSSTransitionReplace extends React.Component {
209217
} = this.props
210218

211219
if (height !== null) {
212-
const heightClassName = (typeof transitionName == 'object' && transitionName !== null)
220+
const heightClassName = (typeof transitionName === 'object' && transitionName !== null)
213221
? transitionName.height || ''
214222
: `${transitionName}-height`
215223

@@ -227,6 +235,12 @@ export default class ReactCSSTransitionReplace extends React.Component {
227235
}
228236

229237
Object.keys(prevChildren).forEach(key => {
238+
const child = prevChildren[key]
239+
const isCallbackRef = typeof child.ref !== 'string'
240+
warning(isCallbackRef,
241+
'string refs are not supported on children of ReactCSSTransitionReplace and will be ignored. ' +
242+
'Please use a callback ref instead: https://facebook.github.io/react/docs/refs-and-the-dom.html#the-ref-callback-attribute')
243+
230244
childrenToRender.push(
231245
React.createElement(childComponent,
232246
{
@@ -240,19 +254,38 @@ export default class ReactCSSTransitionReplace extends React.Component {
240254
},
241255
},
242256
this.wrapChild(
243-
typeof prevChildren[key].type == 'string'
244-
? prevChildren[key]
245-
: React.cloneElement(prevChildren[key], {isLeaving: true}),
246-
{ref: key})
257+
typeof child.type !== 'string'
258+
? React.cloneElement(child, {isLeaving: true})
259+
: child,
260+
{
261+
ref: chain(
262+
isCallbackRef ? child.ref : null,
263+
(r) => {this.childRefs[key] = r}
264+
),
265+
}
266+
)
247267
)
248268
)
249269
})
250270

251271
if (currentChild) {
272+
const isCallbackRef = typeof currentChild.ref !== 'string'
273+
warning(isCallbackRef,
274+
'string refs are not supported on children of ReactCSSTransitionReplace and will be ignored. ' +
275+
'Please use a callback ref instead: https://facebook.github.io/react/docs/refs-and-the-dom.html#the-ref-callback-attribute')
276+
252277
childrenToRender.push(
253278
React.createElement(childComponent,
254279
{key: currentKey},
255-
this.wrapChild(currentChild, {ref: currentKey})
280+
this.wrapChild(
281+
currentChild,
282+
{
283+
ref: chain(
284+
isCallbackRef ? currentChild.ref : null,
285+
(r) => {this.childRefs[currentKey] = r}
286+
),
287+
}
288+
)
256289
)
257290
)
258291
}

0 commit comments

Comments
 (0)