diff --git a/src/addons/ReactFragment.js b/src/addons/ReactFragment.js
new file mode 100644
index 0000000000..e6afbd75f4
--- /dev/null
+++ b/src/addons/ReactFragment.js
@@ -0,0 +1,164 @@
+/**
+ * Copyright 2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+* @providesModule ReactFragment
+*/
+
+'use strict';
+
+var ReactElement = require('ReactElement');
+
+var warning = require('warning');
+
+/**
+ * We used to allow keyed objects to serve as a collection of ReactElements,
+ * or nested sets. This allowed us a way to explicitly key a set a fragment of
+ * components. This is now being replaced with an opaque data structure.
+ * The upgrade path is to call React.addons.createFragment({ key: value }) to
+ * create a keyed fragment. The resulting data structure is opaque, for now.
+ */
+
+if (__DEV__) {
+ var fragmentKey = '_reactFragment';
+ var didWarnKey = '_reactDidWarn';
+ var canWarnForReactFragment = false;
+
+ try {
+ // Feature test. Don't even try to issue this warning if we can't use
+ // enumerable: false.
+
+ var dummy = function() {
+ return 1;
+ };
+
+ Object.defineProperty(
+ {},
+ fragmentKey,
+ { enumerable: false, value: true }
+ );
+
+ Object.defineProperty(
+ {},
+ 'key',
+ { enumerable: true, get: dummy }
+ );
+
+ canWarnForReactFragment = true;
+ } catch (x) { }
+
+ var proxyPropertyAccessWithWarning = function(obj, key) {
+ Object.defineProperty(obj, key, {
+ enumerable: true,
+ get: function() {
+ warning(
+ this[didWarnKey],
+ 'A ReactFragment is an opaque type. Accessing any of its ' +
+ 'properties is deprecated. Pass it to one of the React.Children ' +
+ 'helpers.'
+ );
+ this[didWarnKey] = true;
+ return this[fragmentKey][key];
+ },
+ set: function(value) {
+ warning(
+ this[didWarnKey],
+ 'A ReactFragment is an immutable opaque type. Mutating its ' +
+ 'properties is deprecated.'
+ );
+ this[didWarnKey] = true;
+ this[fragmentKey][key] = value;
+ }
+ });
+ };
+
+ var issuedWarnings = {};
+
+ var didWarnForFragment = function(fragment) {
+ // We use the keys and the type of the value as a heuristic to dedupe the
+ // warning to avoid spamming too much.
+ var fragmentCacheKey = '';
+ for (var key in fragment) {
+ fragmentCacheKey += key + ':' + (typeof fragment[key]) + ',';
+ }
+ var alreadyWarnedOnce = !!issuedWarnings[fragmentCacheKey];
+ issuedWarnings[fragmentCacheKey] = true;
+ return alreadyWarnedOnce;
+ };
+}
+
+var ReactFragment = {
+ // Wrap a keyed object in an opaque proxy that warns you if you access any
+ // of its properties.
+ create: function(object) {
+ if (__DEV__) {
+ if (canWarnForReactFragment) {
+ var proxy = {};
+ Object.defineProperty(proxy, fragmentKey, {
+ enumerable: false,
+ value: object
+ });
+ Object.defineProperty(proxy, didWarnKey, {
+ writable: true,
+ enumerable: false,
+ value: false
+ });
+ for (var key in object) {
+ proxyPropertyAccessWithWarning(proxy, key);
+ }
+ Object.preventExtensions(proxy);
+ return proxy;
+ }
+ }
+ return object;
+ },
+ // Extract the original keyed object from the fragment opaque type. Warn if
+ // a plain object is passed here.
+ extract: function(fragment) {
+ if (__DEV__) {
+ if (canWarnForReactFragment) {
+ if (!fragment[fragmentKey]) {
+ warning(
+ didWarnForFragment(fragment),
+ 'Any use of a keyed object should be wrapped in ' +
+ 'React.addons.createFragment(object) before passed as a child.'
+ );
+ return fragment;
+ }
+ return fragment[fragmentKey];
+ }
+ }
+ return fragment;
+ },
+ // Check if this is a fragment and if so, extract the keyed object. If it
+ // is a fragment-like object, warn that it should be wrapped. Ignore if we
+ // can't determine what kind of object this is.
+ extractIfFragment: function(fragment) {
+ if (__DEV__) {
+ if (canWarnForReactFragment) {
+ // If it is the opaque type, return the keyed object.
+ if (fragment[fragmentKey]) {
+ return fragment[fragmentKey];
+ }
+ // Otherwise, check each property if it has an element, if it does
+ // it is probably meant as a fragment, so we can warn early. Defer,
+ // the warning to extract.
+ for (var key in fragment) {
+ if (fragment.hasOwnProperty(key) &&
+ ReactElement.isValidElement(fragment[key])) {
+ // This looks like a fragment object, we should provide an
+ // early warning.
+ return ReactFragment.extract(fragment);
+ }
+ }
+ }
+ }
+ return fragment;
+ }
+};
+
+module.exports = ReactFragment;
diff --git a/src/addons/__tests__/ReactFragment-test.js b/src/addons/__tests__/ReactFragment-test.js
new file mode 100644
index 0000000000..d7025f8029
--- /dev/null
+++ b/src/addons/__tests__/ReactFragment-test.js
@@ -0,0 +1,76 @@
+/**
+ * Copyright 2015, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ *
+ * @emails react-core
+ */
+
+'use strict';
+
+var React;
+var ReactFragment;
+
+describe('ReactFragment', function() {
+
+ beforeEach(function() {
+ React = require('React');
+ ReactFragment = require('ReactFragment');
+ });
+
+ it('should warn if a plain object is used as a child', function() {
+ spyOn(console, 'warn');
+ var children = {
+ x: ,
+ y:
+ };
+
diff --git a/src/core/__tests__/ReactMultiChildText-test.js b/src/core/__tests__/ReactMultiChildText-test.js
index 0efdc812fc..764dd1b746 100644
--- a/src/core/__tests__/ReactMultiChildText-test.js
+++ b/src/core/__tests__/ReactMultiChildText-test.js
@@ -16,10 +16,13 @@
require('mock-modules');
var React = require('React');
+var ReactFragment = require('ReactFragment');
var ReactTestUtils = require('ReactTestUtils');
var reactComponentExpect = require('reactComponentExpect');
+var frag = ReactFragment.create;
+
// Helpers
var testAllPermutations = function(testCases) {
for (var i = 0; i < testCases.length; i += 2) {
@@ -182,18 +185,18 @@ describe('ReactMultiChildText', function() {
['', 'foo', [true,
, 1.2, ''], 'foo'], ['', 'foo',
, '1.2', '', 'foo'],
// values inside objects
- [{a: true}, {a: true}], [],
- [{a: 1.2}, {a: 1.2}], ['1.2', '1.2'],
- [{a: ''}, {a: ''}], ['', ''],
- [{a: 'foo'}, {a: 'foo'}], ['foo', 'foo'],
- [{a:
}, {a:
}], [
,
],
+ [frag({a: true}), frag({a: true})], [],
+ [frag({a: 1.2}), frag({a: 1.2})], ['1.2', '1.2'],
+ [frag({a: ''}), frag({a: ''})], ['', ''],
+ [frag({a: 'foo'}), frag({a: 'foo'})], ['foo', 'foo'],
+ [frag({a:
}), frag({a:
})], [
,
],
- [{a: true, b: 1.2, c:
}, '', 'foo'], ['1.2',
, '', 'foo'],
- [1.2, '', {a:
, b: 'foo', c: true}], ['1.2', '',
, 'foo'],
- ['', {a: 'foo', b:
, c: true}, 1.2], ['', 'foo',
, '1.2'],
+ [frag({a: true, b: 1.2, c:
}), '', 'foo'], ['1.2',
, '', 'foo'],
+ [1.2, '', frag({a:
, b: 'foo', c: true})], ['1.2', '',
, 'foo'],
+ ['', frag({a: 'foo', b:
, c: true}), 1.2], ['', 'foo',
, '1.2'],
- [true, {a: 1.2, b: '', c:
, d: 'foo'}, true, 1.2], ['1.2', '',
, 'foo', '1.2'],
- ['', 'foo', {a: true, b:
, c: 1.2, d: ''}, 'foo'], ['', 'foo',
, '1.2', '', 'foo'],
+ [true, frag({a: 1.2, b: '', c:
, d: 'foo'}), true, 1.2], ['1.2', '',
, 'foo', '1.2'],
+ ['', 'foo', frag({a: true, b:
, c: 1.2, d: ''}), 'foo'], ['', 'foo',
, '1.2', '', 'foo'],
// values inside elements
[
, '', 'foo'], [
, '', 'foo'],
diff --git a/src/modern/element/__tests__/ReactJSXElementValidator-test.js b/src/modern/element/__tests__/ReactJSXElementValidator-test.js
index c1c05fb946..d664a24d27 100644
--- a/src/modern/element/__tests__/ReactJSXElementValidator-test.js
+++ b/src/modern/element/__tests__/ReactJSXElementValidator-test.js
@@ -15,6 +15,7 @@
// of dynamic errors when using JSX with Flow.
var React;
+var ReactFragment;
var ReactTestUtils;
describe('ReactJSXElementValidator', function() {
@@ -24,6 +25,7 @@ describe('ReactJSXElementValidator', function() {
require('mock-modules').dumpCache();
React = require('React');
+ ReactFragment = require('ReactFragment');
ReactTestUtils = require('ReactTestUtils');
Component = class {
@@ -31,6 +33,10 @@ describe('ReactJSXElementValidator', function() {
};
});
+ function frag(obj) {
+ return ReactFragment.create(obj);
+ }
+
it('warns for keys for arrays of elements in children position', function() {
spyOn(console, 'warn');
@@ -128,7 +134,7 @@ describe('ReactJSXElementValidator', function() {
it('warns for numeric keys on objects as children', function() {
spyOn(console, 'warn');
-
{{ 1: , 2: }};
+
{frag({ 1: , 2: })};
expect(console.warn.argsForCall.length).toBe(1);
expect(console.warn.argsForCall[0][0]).toContain(
diff --git a/src/utils/ReactChildren.js b/src/utils/ReactChildren.js
index 24edcd42e6..4109557dde 100644
--- a/src/utils/ReactChildren.js
+++ b/src/utils/ReactChildren.js
@@ -12,6 +12,7 @@
'use strict';
var PooledClass = require('PooledClass');
+var ReactFragment = require('ReactFragment');
var traverseAllChildren = require('traverseAllChildren');
var warning = require('warning');
@@ -121,7 +122,7 @@ function mapChildren(children, func, context) {
var traverseContext = MapBookKeeping.getPooled(mapResult, func, context);
traverseAllChildren(children, mapSingleChildIntoContext, traverseContext);
MapBookKeeping.release(traverseContext);
- return mapResult;
+ return ReactFragment.create(mapResult);
}
function forEachSingleChildDummy(traverseContext, child, name, i) {
diff --git a/src/utils/__tests__/ReactChildren-test.js b/src/utils/__tests__/ReactChildren-test.js
index 550af41eb7..8a271de6be 100644
--- a/src/utils/__tests__/ReactChildren-test.js
+++ b/src/utils/__tests__/ReactChildren-test.js
@@ -14,12 +14,31 @@
describe('ReactChildren', function() {
var ReactChildren;
var React;
+ var ReactFragment;
beforeEach(function() {
ReactChildren = require('ReactChildren');
React = require('React');
+ ReactFragment = require('ReactFragment');
});
+ function frag(obj) {
+ return ReactFragment.create(obj);
+ }
+
+ function nthChild(mappedChildren, n) {
+ var result = null;
+ ReactChildren.forEach(mappedChildren, function(child, index) {
+ if (index === n) {
+ result = child;
+ }
+ });
+ return result;
+ }
+
+ function keyOfNthChild(mappedChildren, n) {
+ return Object.keys(ReactFragment.extract(mappedChildren))[n];
+ }
it('should support identity for simple', function() {
var callback = jasmine.createSpy().andCallFake(function(kid, index) {
@@ -37,7 +56,7 @@ describe('ReactChildren', function() {
callback.reset();
var mappedChildren = ReactChildren.map(instance.props.children, callback);
expect(callback).toHaveBeenCalledWith(simpleKid, 0);
- expect(mappedChildren[Object.keys(mappedChildren)[0]]).toBe(simpleKid);
+ expect(nthChild(mappedChildren, 0)).toBe(simpleKid);
});
it('should treat single arrayless child as being in array', function() {
@@ -52,7 +71,7 @@ describe('ReactChildren', function() {
callback.reset();
var mappedChildren = ReactChildren.map(instance.props.children, callback);
expect(callback).toHaveBeenCalledWith(simpleKid, 0);
- expect(mappedChildren[Object.keys(mappedChildren)[0]]).toBe(simpleKid);
+ expect(nthChild(mappedChildren, 0)).toBe(simpleKid);
});
it('should treat single child in array as expected', function() {
@@ -67,7 +86,7 @@ describe('ReactChildren', function() {
callback.reset();
var mappedChildren = ReactChildren.map(instance.props.children, callback);
expect(callback).toHaveBeenCalledWith(simpleKid, 0);
- expect(mappedChildren[Object.keys(mappedChildren)[0]]).toBe(simpleKid);
+ expect(nthChild(mappedChildren, 0)).toBe(simpleKid);
});
it('should pass key to returned component', function() {
@@ -80,11 +99,10 @@ describe('ReactChildren', function() {
var instance =
{simpleKid}
;
var mappedChildren = ReactChildren.map(instance.props.children, mapFn);
- var mappedKeys = Object.keys(mappedChildren);
- expect(mappedKeys.length).toBe(1);
- expect(mappedChildren[mappedKeys[0]]).not.toBe(simpleKid);
- expect(mappedChildren[mappedKeys[0]].props.children).toBe(simpleKid);
- expect(mappedKeys[0]).toBe('.$simple');
+ expect(ReactChildren.count(mappedChildren)).toBe(1);
+ expect(nthChild(mappedChildren, 0)).not.toBe(simpleKid);
+ expect(nthChild(mappedChildren, 0).props.children).toBe(simpleKid);
+ expect(keyOfNthChild(mappedChildren, 0)).toBe('.$simple');
});
it('should invoke callback with the right context', function() {
@@ -94,7 +112,8 @@ describe('ReactChildren', function() {
return this;
};
- var scopeTester = {};
+ // TODO: Use an object to test, after non-object fragments has fully landed.
+ var scopeTester = 'scope tester';
var simpleKid =
;
var instance =
{simpleKid}
;
@@ -104,9 +123,8 @@ describe('ReactChildren', function() {
var mappedChildren =
ReactChildren.map(instance.props.children, callback, scopeTester);
- var mappedKeys = Object.keys(mappedChildren);
- expect(mappedKeys.length).toBe(1);
- expect(mappedChildren[mappedKeys[0]]).toBe(scopeTester);
+ expect(ReactChildren.count(mappedChildren)).toBe(1);
+ expect(nthChild(mappedChildren, 0)).toBe(scopeTester);
});
it('should be called for each child', function() {
@@ -149,28 +167,33 @@ describe('ReactChildren', function() {
var mappedChildren =
ReactChildren.map(instance.props.children, callback);
- var mappedKeys = Object.keys(mappedChildren);
expect(callback.calls.length).toBe(5);
- expect(mappedKeys.length).toBe(5);
+ expect(ReactChildren.count(mappedChildren)).toBe(5);
// Keys default to indices.
- expect(mappedKeys).toEqual(
+ expect([
+ keyOfNthChild(mappedChildren, 0),
+ keyOfNthChild(mappedChildren, 1),
+ keyOfNthChild(mappedChildren, 2),
+ keyOfNthChild(mappedChildren, 3),
+ keyOfNthChild(mappedChildren, 4)
+ ]).toEqual(
['.$keyZero', '.1', '.$keyTwo', '.3', '.$keyFour']
);
expect(callback).toHaveBeenCalledWith(zero, 0);
- expect(mappedChildren[mappedKeys[0]]).toBe(zeroMapped);
+ expect(nthChild(mappedChildren, 0)).toBe(zeroMapped);
expect(callback).toHaveBeenCalledWith(one, 1);
- expect(mappedChildren[mappedKeys[1]]).toBe(oneMapped);
+ expect(nthChild(mappedChildren, 1)).toBe(oneMapped);
expect(callback).toHaveBeenCalledWith(two, 2);
- expect(mappedChildren[mappedKeys[2]]).toBe(twoMapped);
+ expect(nthChild(mappedChildren, 2)).toBe(twoMapped);
expect(callback).toHaveBeenCalledWith(three, 3);
- expect(mappedChildren[mappedKeys[3]]).toBe(threeMapped);
+ expect(nthChild(mappedChildren, 3)).toBe(threeMapped);
expect(callback).toHaveBeenCalledWith(four, 4);
- expect(mappedChildren[mappedKeys[4]]).toBe(fourMapped);
+ expect(nthChild(mappedChildren, 4)).toBe(fourMapped);
});
@@ -204,11 +227,11 @@ describe('ReactChildren', function() {
var instance = (
{
- [{
+ [frag({
firstHalfKey: [zero, one, two],
secondHalfKey: [three, four],
keyFive: five
- }]
+ })]
}
);
@@ -222,11 +245,17 @@ describe('ReactChildren', function() {
callback.reset();
var mappedChildren = ReactChildren.map(instance.props.children, callback);
- var mappedKeys = Object.keys(mappedChildren);
expect(callback.calls.length).toBe(6);
- expect(mappedKeys.length).toBe(6);
+ expect(ReactChildren.count(mappedChildren)).toBe(6);
// Keys default to indices.
- expect(mappedKeys).toEqual([
+ expect([
+ keyOfNthChild(mappedChildren, 0),
+ keyOfNthChild(mappedChildren, 1),
+ keyOfNthChild(mappedChildren, 2),
+ keyOfNthChild(mappedChildren, 3),
+ keyOfNthChild(mappedChildren, 4),
+ keyOfNthChild(mappedChildren, 5)
+ ]).toEqual([
'.0:$firstHalfKey:0:$keyZero',
'.0:$firstHalfKey:0:1',
'.0:$firstHalfKey:0:$keyTwo',
@@ -236,22 +265,22 @@ describe('ReactChildren', function() {
]);
expect(callback).toHaveBeenCalledWith(zero, 0);
- expect(mappedChildren[mappedKeys[0]]).toBe(zeroMapped);
+ expect(nthChild(mappedChildren, 0)).toBe(zeroMapped);
expect(callback).toHaveBeenCalledWith(one, 1);
- expect(mappedChildren[mappedKeys[1]]).toBe(oneMapped);
+ expect(nthChild(mappedChildren, 1)).toBe(oneMapped);
expect(callback).toHaveBeenCalledWith(two, 2);
- expect(mappedChildren[mappedKeys[2]]).toBe(twoMapped);
+ expect(nthChild(mappedChildren, 2)).toBe(twoMapped);
expect(callback).toHaveBeenCalledWith(three, 3);
- expect(mappedChildren[mappedKeys[3]]).toBe(threeMapped);
+ expect(nthChild(mappedChildren, 3)).toBe(threeMapped);
expect(callback).toHaveBeenCalledWith(four, 4);
- expect(mappedChildren[mappedKeys[4]]).toBe(fourMapped);
+ expect(nthChild(mappedChildren, 4)).toBe(fourMapped);
expect(callback).toHaveBeenCalledWith(five, 5);
- expect(mappedChildren[mappedKeys[5]]).toBe(fiveMapped);
+ expect(nthChild(mappedChildren, 5)).toBe(fiveMapped);
});
it('should retain key across two mappings', function() {
@@ -325,7 +354,8 @@ describe('ReactChildren', function() {
var mapped = ReactChildren.map(instance.props.children, mapFn);
expect(console.warn.calls.length).toEqual(1);
- expect(mapped).toEqual({'.$something': zero});
+ expect(nthChild(mapped, 0)).toBe(zero);
+ expect(keyOfNthChild(mapped, 0)).toBe('.$something');
});
it('should return 0 for null children', function() {
@@ -380,14 +410,49 @@ describe('ReactChildren', function() {
var instance = (
{
- [{
+ [frag({
firstHalfKey: [zero, one, two],
secondHalfKey: [three, four],
keyFive: five
- }]
+ })]
}
);
var numberOfChildren = ReactChildren.count(instance.props.children);
expect(numberOfChildren).toBe(6);
});
+
+ it('should warn if a fragment is used without the wrapper', function () {
+ spyOn(console, 'warn');
+ var child = React.createElement('span');
+ ReactChildren.forEach({ a: child, b: child}, function(c) {
+ expect(c).toBe(child);
+ });
+ expect(console.warn.calls.length).toBe(1);
+ expect(console.warn.calls[0].args[0]).toContain('use of a keyed object');
+ });
+
+ it('should warn if a fragment is accessed', function () {
+ spyOn(console, 'warn');
+ var child = React.createElement('span');
+ var frag = ReactChildren.map([ child, child ], function(c) {
+ return c;
+ });
+ for (var key in frag) {
+ frag[key];
+ break;
+ }
+ expect(console.warn.calls.length).toBe(1);
+ expect(console.warn.calls[0].args[0]).toContain('is an opaque type');
+
+ var frag2 = ReactChildren.map([ child, child ], function(c) {
+ return c;
+ });
+ for (var key in frag2) {
+ frag2[key] = 123;
+ break;
+ }
+ expect(console.warn.calls.length).toBe(2);
+ expect(console.warn.calls[1].args[0]).toContain('is an immutable opaque');
+ });
+
});
diff --git a/src/utils/__tests__/onlyChild-test.js b/src/utils/__tests__/onlyChild-test.js
index 835bf794d9..29a7c77226 100644
--- a/src/utils/__tests__/onlyChild-test.js
+++ b/src/utils/__tests__/onlyChild-test.js
@@ -14,11 +14,13 @@
describe('onlyChild', function() {
var React;
+ var ReactFragment;
var onlyChild;
var WrapComponent;
beforeEach(function() {
React = require('React');
+ ReactFragment = require('ReactFragment');
onlyChild = require('onlyChild');
WrapComponent = React.createClass({
render: function() {
@@ -64,7 +66,7 @@ describe('onlyChild', function() {
expect(function() {
var instance =
- {{oneThing: }}
+ {ReactFragment.create({oneThing: })}
;
onlyChild(instance.props.children);
}).toThrow();
diff --git a/src/utils/__tests__/sliceChildren-test.js b/src/utils/__tests__/sliceChildren-test.js
index 47afb17d86..065da44697 100644
--- a/src/utils/__tests__/sliceChildren-test.js
+++ b/src/utils/__tests__/sliceChildren-test.js
@@ -14,6 +14,7 @@
describe('sliceChildren', function() {
var React;
+ var ReactFragment;
var ReactTestUtils;
var sliceChildren;
@@ -23,6 +24,7 @@ describe('sliceChildren', function() {
beforeEach(function() {
React = require('React');
+ ReactFragment = require('ReactFragment');
ReactTestUtils = require('ReactTestUtils');
sliceChildren = require('sliceChildren');
@@ -52,6 +54,11 @@ describe('sliceChildren', function() {
return rendered.props.children;
}
+ function testKeyValuePairs(children, expectedPairs) {
+ var obj = ReactFragment.extract(children);
+ expect(obj).toEqual(expectedPairs);
+ }
+
it('should render the whole set if start zero is supplied', function() {
var fullSet = [
,
@@ -59,7 +66,7 @@ describe('sliceChildren', function() {
];
var children = renderAndSlice(fullSet, 0);
- expect(children).toEqual({
+ testKeyValuePairs(children, {
'.$A': fullSet[0],
'.$B': fullSet[1],
'.$C': fullSet[2]
@@ -73,7 +80,7 @@ describe('sliceChildren', function() {
];
var children = renderAndSlice(fullSet, 1);
- expect(children).toEqual({
+ testKeyValuePairs(children, {
'.$B': fullSet[1],
'.$C': fullSet[2]
});
@@ -87,7 +94,7 @@ describe('sliceChildren', function() {
];
var children = renderAndSlice(fullSet, 1, 2);
- expect(children).toEqual({
+ testKeyValuePairs(children, {
'.$B': fullSet[1]
});
});
@@ -103,7 +110,7 @@ describe('sliceChildren', function() {
.expectRenderedChild()
.instance();
- expect(rendered.props.children).toEqual({
+ testKeyValuePairs(rendered.props.children, {
'.1': b
});
});
diff --git a/src/utils/__tests__/traverseAllChildren-test.js b/src/utils/__tests__/traverseAllChildren-test.js
index c32e07c66d..91b51f0292 100644
--- a/src/utils/__tests__/traverseAllChildren-test.js
+++ b/src/utils/__tests__/traverseAllChildren-test.js
@@ -14,11 +14,16 @@
describe('traverseAllChildren', function() {
var traverseAllChildren;
var React;
+ var ReactFragment;
beforeEach(function() {
traverseAllChildren = require('traverseAllChildren');
React = require('React');
+ ReactFragment = require('ReactFragment');
});
+ function frag(obj) {
+ return ReactFragment.create(obj);
+ }
it('should support identity for simple', function() {
var traverseContext = [];
@@ -139,8 +144,8 @@ describe('traverseAllChildren', function() {
var instance = (
{div}
- {[{span}]}
- {{a: a}}
+ {[frag({span})]}
+ {frag({a: a})}
{'string'}
{1234}
{true}
@@ -206,11 +211,11 @@ describe('traverseAllChildren', function() {
var instance = (
{
- [{
+ [frag({
firstHalfKey: [zero, one, two],
secondHalfKey: [three, four],
keyFive: five
- }]
+ })]
}
);
diff --git a/src/utils/sliceChildren.js b/src/utils/sliceChildren.js
index fee13fc6c0..22a01dea14 100644
--- a/src/utils/sliceChildren.js
+++ b/src/utils/sliceChildren.js
@@ -11,6 +11,8 @@
'use strict';
+var ReactFragment = require('ReactFragment');
+
var flattenChildren = require('flattenChildren');
/**
@@ -43,7 +45,7 @@ function sliceChildren(children, start, end) {
break;
}
}
- return slicedChildren;
+ return ReactFragment.create(slicedChildren);
}
module.exports = sliceChildren;
diff --git a/src/utils/traverseAllChildren.js b/src/utils/traverseAllChildren.js
index d0a31fd489..da8fd53bb9 100644
--- a/src/utils/traverseAllChildren.js
+++ b/src/utils/traverseAllChildren.js
@@ -12,6 +12,7 @@
'use strict';
var ReactElement = require('ReactElement');
+var ReactFragment = require('ReactFragment');
var ReactInstanceHandles = require('ReactInstanceHandles');
var getIteratorFn = require('getIteratorFn');
@@ -184,9 +185,10 @@ function traverseAllChildrenImpl(
'traverseAllChildren(...): Encountered an invalid child; DOM ' +
'elements are not valid children of React components.'
);
- for (var key in children) {
- if (children.hasOwnProperty(key)) {
- child = children[key];
+ var fragment = ReactFragment.extract(children);
+ for (var key in fragment) {
+ if (fragment.hasOwnProperty(key)) {
+ child = fragment[key];
nextName = (
(nameSoFar !== '' ? nameSoFar + SUBSEPARATOR : SEPARATOR) +
wrapUserProvidedKey(key) + SUBSEPARATOR +