diff --git a/src/TransitionGroup.js b/src/TransitionGroup.js
index 35a8476b..2cba6a6a 100644
--- a/src/TransitionGroup.js
+++ b/src/TransitionGroup.js
@@ -191,24 +191,33 @@ class TransitionGroup extends React.Component {
let child = this.state.children[key];
if (child) {
let isCallbackRef = typeof child.ref !== 'string';
+ let factoryChild = this.props.childFactory(child);
+ let ref = (r) => {
+ this.childRefs[key] = r;
+ };
+
warning(isCallbackRef,
'string refs are not supported on children of TransitionGroup and will be ignored. ' +
'Please use a callback ref instead: https://facebook.github.io/react/docs/refs-and-the-dom.html#the-ref-callback-attribute');
+ // Always chaining the refs leads to problems when the childFactory
+ // wraps the child. The child ref callback gets called twice with the
+ // wrapper and the child. So we only need to chain the ref if the
+ // factoryChild is not different from child.
+ if (factoryChild === child && isCallbackRef) {
+ ref = chain(child.ref, ref);
+ }
+
// You may need to apply reactive updates to a child as it is leaving.
// The normal React way to do it won't work since the child will have
// already been removed. In case you need this behavior you can provide
// a childFactory function to wrap every child, even the ones that are
// leaving.
childrenToRender.push(React.cloneElement(
- this.props.childFactory(child),
+ factoryChild,
{
key,
- ref: chain(
- isCallbackRef ? child.ref : null,
- (r) => {
- this.childRefs[key] = r;
- }),
+ ref,
},
));
}
diff --git a/test/TransitionGroup-test.js b/test/TransitionGroup-test.js
index bbe88031..6ea1e7d1 100644
--- a/test/TransitionGroup-test.js
+++ b/test/TransitionGroup-test.js
@@ -1,6 +1,7 @@
import tsp from 'teaspoon';
let React;
+let PropTypes;
let ReactDOM;
let TransitionGroup;
@@ -11,6 +12,7 @@ describe('TransitionGroup', () => {
beforeEach(() => {
React = require('react');
+ PropTypes = require('prop-types');
ReactDOM = require('react-dom');
TransitionGroup = require('../src/TransitionGroup');
@@ -72,6 +74,80 @@ describe('TransitionGroup', () => {
expect(ref).toHaveBeenCalled();
});
+ it('properly calls ref if childFactory doesn\'t create a wrapper', () => {
+ let transition;
+
+ const transitionRef = (r) => { transition = r; };
+ const childRef = jest.fn();
+
+ class Child extends React.Component {
+ render() {
+ return ();
+ }
+ }
+
+ const rendered = tsp(
+
+
+ ,
+ )
+ .render();
+
+ expect(transition.childRefs['.$child']).toEqual(jasmine.any(Child));
+
+ rendered.unmount();
+
+ for (let i in childRef.mock.calls) {
+ let call = childRef.mock.calls[i];
+ let valid = (call[0] === null || call[0] instanceof Child) && call.length === 1;
+
+ expect(valid).toBeTruthy();
+ }
+ });
+
+ it('properly calls ref if childFactory does create a wrapper', () => {
+ let transition;
+
+ const transitionRef = (r) => { transition = r; };
+ const childRef = jest.fn();
+
+ class Wrapper extends React.Component {
+ static propTypes = {
+ children: PropTypes.element,
+ }
+
+ render() {
+ return this.props.children;
+ }
+ }
+
+ class Child extends React.Component {
+ render() {
+ return ();
+ }
+ }
+
+ const childFactory = x => React.createElement(Wrapper, null, x);
+
+ const rendered = tsp(
+
+
+ ,
+ )
+ .render();
+
+ expect(transition.childRefs['.$child']).toEqual(jasmine.any(Wrapper));
+
+ rendered.unmount();
+
+ for (let i in childRef.mock.calls) {
+ let call = childRef.mock.calls[i];
+ let valid = (call[0] === null || call[0] instanceof Child) && call.length === 1;
+
+ expect(valid).toBeTruthy();
+ }
+ });
+
it('should handle willEnter correctly', () => {
let log = [];