mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Make createFragment return an array, disallow objects as children
This commit is contained in:
+40
-134
@@ -13,6 +13,8 @@
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
|
||||
var invariant = require('invariant');
|
||||
var traverseAllChildren = require('traverseAllChildren');
|
||||
var warning = require('warning');
|
||||
|
||||
/**
|
||||
@@ -20,86 +22,26 @@ var warning = require('warning');
|
||||
* 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.
|
||||
* create a keyed fragment. The resulting data structure is an array.
|
||||
*/
|
||||
|
||||
var fragmentKey;
|
||||
var didWarnKey;
|
||||
var canWarnForReactFragment;
|
||||
var numericPropertyRegex = /^\d+$/;
|
||||
|
||||
if (__DEV__) {
|
||||
fragmentKey = '_reactFragment';
|
||||
didWarnKey = '_reactDidWarn';
|
||||
|
||||
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) {
|
||||
canWarnForReactFragment = false;
|
||||
}
|
||||
|
||||
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 getFragmentKeyString = function(fragment) {
|
||||
var fragmentCacheKey = '';
|
||||
for (var key in fragment) {
|
||||
fragmentCacheKey += key + ':' + (typeof fragment[key]) + ',';
|
||||
}
|
||||
return fragmentCacheKey;
|
||||
};
|
||||
|
||||
var didWarnForFragment = function(fragmentCacheKey) {
|
||||
// We use the keys and the type of the value as a heuristic to dedupe the
|
||||
// warning to avoid spamming too much.
|
||||
var alreadyWarnedOnce = !!issuedWarnings[fragmentCacheKey];
|
||||
issuedWarnings[fragmentCacheKey] = true;
|
||||
return alreadyWarnedOnce;
|
||||
};
|
||||
var userProvidedKeyEscapeRegex = /\//g;
|
||||
function escapeUserProvidedKey(text) {
|
||||
return ('' + text).replace(userProvidedKeyEscapeRegex, '//');
|
||||
}
|
||||
|
||||
function processSingleChildWithContext(ctx, child, childKey) {
|
||||
if (ReactElement.isValidElement(child)) {
|
||||
child = ReactElement.cloneAndReplaceKey(child, ctx.prefix + childKey);
|
||||
}
|
||||
// For text components, leave unkeyed
|
||||
ctx.result.push(child);
|
||||
}
|
||||
|
||||
var warnedAboutNumeric = false;
|
||||
|
||||
var ReactFragment = {
|
||||
// Wrap a keyed object in an opaque proxy that warns you if you access any
|
||||
// of its properties.
|
||||
@@ -121,71 +63,35 @@ var ReactFragment = {
|
||||
);
|
||||
return object;
|
||||
}
|
||||
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]) {
|
||||
var fragmentKeys = getFragmentKeyString(fragment);
|
||||
invariant(
|
||||
object.nodeType !== 1,
|
||||
'React.addons.createFragment(...): Encountered an invalid child; DOM ' +
|
||||
'elements are not valid children of React components.'
|
||||
);
|
||||
|
||||
var result = [];
|
||||
var context = {
|
||||
result: result,
|
||||
prefix: '',
|
||||
};
|
||||
|
||||
for (var key in object) {
|
||||
if (__DEV__) {
|
||||
if (!warnedAboutNumeric && numericPropertyRegex.test(key)) {
|
||||
warning(
|
||||
didWarnForFragment(fragmentKeys),
|
||||
'Any use of a keyed object should be wrapped in ' +
|
||||
'React.addons.createFragment(object) before being passed as a ' +
|
||||
'child. {%s}',
|
||||
fragmentKeys
|
||||
false,
|
||||
'React.addons.createFragment(...): Child objects should have ' +
|
||||
'non-numeric keys so ordering is preserved.'
|
||||
);
|
||||
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);
|
||||
}
|
||||
warnedAboutNumeric = true;
|
||||
}
|
||||
}
|
||||
context.prefix = escapeUserProvidedKey(key) + '/';
|
||||
traverseAllChildren(object[key], processSingleChildWithContext, context);
|
||||
}
|
||||
return fragment;
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -23,51 +23,23 @@ describe('ReactFragment', function() {
|
||||
ReactFragment = require('ReactFragment');
|
||||
});
|
||||
|
||||
it('should warn if a plain object is used as a child', function() {
|
||||
spyOn(console, 'error');
|
||||
var children = {
|
||||
x: <span />,
|
||||
y: <span />,
|
||||
};
|
||||
void <div>{children}</div>;
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toContain(
|
||||
'Any use of a keyed object'
|
||||
);
|
||||
expect(console.error.calls[0].args[0]).toContain(
|
||||
'{x:object,y:object,}'
|
||||
);
|
||||
// Only warn once for the same set of children
|
||||
var sameChildren = {
|
||||
x: <span />,
|
||||
y: <span />,
|
||||
};
|
||||
void <div>{sameChildren}</div>;
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
it('should warn if a plain object even if it is deep', function() {
|
||||
spyOn(console, 'error');
|
||||
it('should throw if a plain object is used as a child', function() {
|
||||
var children = {
|
||||
x: <span />,
|
||||
y: <span />,
|
||||
z: <span />,
|
||||
};
|
||||
var element = <div>{[children]}</div>;
|
||||
expect(console.error.calls.length).toBe(0);
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(element, container);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toContain(
|
||||
'Any use of a keyed object'
|
||||
);
|
||||
expect(console.error.calls[0].args[0]).toContain(
|
||||
'{x:object,y:object,z:object,}'
|
||||
expect(() => ReactDOM.render(element, container)).toThrow(
|
||||
'Invariant Violation: Objects are not valid as a React child (found ' +
|
||||
'object with keys {x, y, z}). If you meant to render a collection of ' +
|
||||
'children, use an array instead or wrap the object using ' +
|
||||
'React.addons.createFragment(object).'
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn if a plain object even if it is in an owner', function() {
|
||||
spyOn(console, 'error');
|
||||
it('should throw if a plain object even if it is in an owner', function() {
|
||||
class Foo {
|
||||
render() {
|
||||
var children = {
|
||||
@@ -78,27 +50,23 @@ describe('ReactFragment', function() {
|
||||
return <div>{[children]}</div>;
|
||||
}
|
||||
}
|
||||
expect(console.error.calls.length).toBe(0);
|
||||
var container = document.createElement('div');
|
||||
ReactDOM.render(<Foo />, container);
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toContain(
|
||||
'Any use of a keyed object'
|
||||
expect(() => ReactDOM.render(<Foo />, container)).toThrow(
|
||||
'Invariant Violation: Objects are not valid as a React child (found ' +
|
||||
'object with keys {a, b, c}). If you meant to render a collection of ' +
|
||||
'children, use an array instead or wrap the object using ' +
|
||||
'React.addons.createFragment(object). Check the render method of `Foo`.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn if accessing any property on a fragment', function() {
|
||||
it('warns for numeric keys on objects as children', function() {
|
||||
spyOn(console, 'error');
|
||||
var children = {
|
||||
x: <span />,
|
||||
y: <span />,
|
||||
};
|
||||
var frag = ReactFragment.create(children);
|
||||
void frag.x;
|
||||
frag.y = 10;
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toContain(
|
||||
'A ReactFragment is an opaque type'
|
||||
|
||||
ReactFragment.create({1: <span />, 2: <span />});
|
||||
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'Child objects should have non-numeric keys so ordering is preserved.'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,13 +12,12 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactChildren = require('ReactChildren');
|
||||
var ReactFragment = require('ReactFragment');
|
||||
var flattenChildren = require('flattenChildren');
|
||||
|
||||
var ReactTransitionChildMapping = {
|
||||
/**
|
||||
* Given `this.props.children`, return an object mapping key to child. Just
|
||||
* simple syntactic sugar around ReactChildren.map().
|
||||
* simple syntactic sugar around flattenChildren().
|
||||
*
|
||||
* @param {*} children `this.props.children`
|
||||
* @return {object} Mapping of key to child
|
||||
@@ -27,9 +26,7 @@ var ReactTransitionChildMapping = {
|
||||
if (!children) {
|
||||
return children;
|
||||
}
|
||||
return ReactFragment.extract(ReactChildren.map(children, function(child) {
|
||||
return child;
|
||||
}));
|
||||
return flattenChildren(children);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -146,7 +146,7 @@ function countChildren(children, context) {
|
||||
}
|
||||
|
||||
|
||||
function pushSingleChildToArray(traverseContext, child, name, i) {
|
||||
function pushSingleChildToArray(traverseContext, child, name) {
|
||||
if (child == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,20 +22,6 @@ describe('ReactChildren', function() {
|
||||
ReactFragment = require('ReactFragment');
|
||||
});
|
||||
|
||||
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) {
|
||||
return kid;
|
||||
@@ -52,7 +38,7 @@ describe('ReactChildren', function() {
|
||||
callback.reset();
|
||||
var mappedChildren = ReactChildren.map(instance.props.children, callback);
|
||||
expect(callback).toHaveBeenCalledWith(simpleKid, 0);
|
||||
expect(nthChild(mappedChildren, 0)).toBe(simpleKid);
|
||||
expect(mappedChildren[0]).toEqual(<span key=".$simple/.$simple" />);
|
||||
});
|
||||
|
||||
it('should treat single arrayless child as being in array', function() {
|
||||
@@ -67,7 +53,7 @@ describe('ReactChildren', function() {
|
||||
callback.reset();
|
||||
var mappedChildren = ReactChildren.map(instance.props.children, callback);
|
||||
expect(callback).toHaveBeenCalledWith(simpleKid, 0);
|
||||
expect(nthChild(mappedChildren, 0)).toBe(simpleKid);
|
||||
expect(mappedChildren[0]).toEqual(<span key=".0/.0" />);
|
||||
});
|
||||
|
||||
it('should treat single child in array as expected', function() {
|
||||
@@ -82,7 +68,7 @@ describe('ReactChildren', function() {
|
||||
callback.reset();
|
||||
var mappedChildren = ReactChildren.map(instance.props.children, callback);
|
||||
expect(callback).toHaveBeenCalledWith(simpleKid, 0);
|
||||
expect(nthChild(mappedChildren, 0)).toBe(simpleKid);
|
||||
expect(mappedChildren[0]).toEqual(<span key=".$simple/.$simple" />);
|
||||
|
||||
});
|
||||
|
||||
@@ -97,9 +83,9 @@ describe('ReactChildren', function() {
|
||||
var mappedChildren = ReactChildren.map(instance.props.children, mapFn);
|
||||
|
||||
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');
|
||||
expect(mappedChildren[0]).not.toBe(simpleKid);
|
||||
expect(mappedChildren[0].props.children).toBe(simpleKid);
|
||||
expect(mappedChildren[0].key).toBe('.$simple/.0');
|
||||
});
|
||||
|
||||
it('should invoke callback with the right context', function() {
|
||||
@@ -121,7 +107,7 @@ describe('ReactChildren', function() {
|
||||
ReactChildren.map(instance.props.children, callback, scopeTester);
|
||||
|
||||
expect(ReactChildren.count(mappedChildren)).toBe(1);
|
||||
expect(nthChild(mappedChildren, 0)).toBe(scopeTester);
|
||||
expect(mappedChildren[0]).toBe(scopeTester);
|
||||
});
|
||||
|
||||
it('should be called for each child', function() {
|
||||
@@ -165,35 +151,30 @@ describe('ReactChildren', function() {
|
||||
var mappedChildren =
|
||||
ReactChildren.map(instance.props.children, callback);
|
||||
expect(callback.calls.length).toBe(5);
|
||||
expect(ReactChildren.count(mappedChildren)).toBe(5);
|
||||
expect(ReactChildren.count(mappedChildren)).toBe(4);
|
||||
// Keys default to indices.
|
||||
expect([
|
||||
keyOfNthChild(mappedChildren, 0),
|
||||
keyOfNthChild(mappedChildren, 1),
|
||||
keyOfNthChild(mappedChildren, 2),
|
||||
keyOfNthChild(mappedChildren, 3),
|
||||
keyOfNthChild(mappedChildren, 4),
|
||||
mappedChildren[0].key,
|
||||
mappedChildren[1].key,
|
||||
mappedChildren[2].key,
|
||||
mappedChildren[3].key,
|
||||
]).toEqual(
|
||||
['.$keyZero', '.1', '.$keyTwo', '.3', '.$keyFour']
|
||||
['.$keyZero/.$giraffe', '.$keyTwo/.0', '.3/.0', '.$keyFour/.$keyFour']
|
||||
);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(zero, 0);
|
||||
expect(nthChild(mappedChildren, 0)).toBe(zeroMapped);
|
||||
expect(mappedChildren[0]).toEqual(<div key=".$keyZero/.$giraffe" />);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(one, 1);
|
||||
expect(nthChild(mappedChildren, 1)).toBe(oneMapped);
|
||||
expect(mappedChildren[1]).toEqual(<div key=".$keyTwo/.0" />);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(two, 2);
|
||||
expect(nthChild(mappedChildren, 2)).toBe(twoMapped);
|
||||
expect(mappedChildren[2]).toEqual(<span key=".3/.0" />);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(three, 3);
|
||||
expect(nthChild(mappedChildren, 3)).toBe(threeMapped);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(four, 4);
|
||||
expect(nthChild(mappedChildren, 4)).toBe(fourMapped);
|
||||
expect(mappedChildren[3]).toEqual(<div key=".$keyFour/.$keyFour" />);
|
||||
});
|
||||
|
||||
|
||||
it('should be called for each child in nested structure', function() {
|
||||
var zero = <div key="keyZero" />;
|
||||
var one = null;
|
||||
@@ -222,64 +203,54 @@ describe('ReactChildren', function() {
|
||||
index === 4 ? fourMapped : fiveMapped;
|
||||
});
|
||||
|
||||
var instance = (
|
||||
<div>{
|
||||
[
|
||||
ReactFragment.create({
|
||||
firstHalfKey: [zero, one, two],
|
||||
secondHalfKey: [three, four],
|
||||
keyFive: five,
|
||||
}),
|
||||
]
|
||||
}</div>
|
||||
);
|
||||
var frag = ReactFragment.create({
|
||||
firstHalfKey: [zero, one, two],
|
||||
secondHalfKey: [three, four],
|
||||
keyFive: five,
|
||||
});
|
||||
var instance = <div>{[frag]}</div>;
|
||||
|
||||
ReactChildren.forEach(instance.props.children, callback);
|
||||
expect(callback).toHaveBeenCalledWith(zero, 0);
|
||||
expect(callback).toHaveBeenCalledWith(one, 1);
|
||||
expect(callback).toHaveBeenCalledWith(two, 2);
|
||||
expect(callback).toHaveBeenCalledWith(three, 3);
|
||||
expect(callback).toHaveBeenCalledWith(four, 4);
|
||||
expect(callback).toHaveBeenCalledWith(five, 5);
|
||||
expect(callback).toHaveBeenCalledWith(frag[0], 0);
|
||||
expect(callback).toHaveBeenCalledWith(frag[1], 1);
|
||||
expect(callback).toHaveBeenCalledWith(frag[2], 2);
|
||||
expect(callback).toHaveBeenCalledWith(frag[3], 3);
|
||||
expect(callback).toHaveBeenCalledWith(frag[4], 4);
|
||||
expect(callback).toHaveBeenCalledWith(frag[5], 5);
|
||||
callback.reset();
|
||||
|
||||
var mappedChildren = ReactChildren.map(instance.props.children, callback);
|
||||
expect(callback.calls.length).toBe(6);
|
||||
expect(ReactChildren.count(mappedChildren)).toBe(6);
|
||||
expect(ReactChildren.count(mappedChildren)).toBe(5);
|
||||
// Keys default to indices.
|
||||
expect([
|
||||
keyOfNthChild(mappedChildren, 0),
|
||||
keyOfNthChild(mappedChildren, 1),
|
||||
keyOfNthChild(mappedChildren, 2),
|
||||
keyOfNthChild(mappedChildren, 3),
|
||||
keyOfNthChild(mappedChildren, 4),
|
||||
keyOfNthChild(mappedChildren, 5),
|
||||
mappedChildren[0].key,
|
||||
mappedChildren[1].key,
|
||||
mappedChildren[2].key,
|
||||
mappedChildren[3].key,
|
||||
mappedChildren[4].key,
|
||||
]).toEqual([
|
||||
'.0:$firstHalfKey:0:$keyZero',
|
||||
'.0:$firstHalfKey:0:1',
|
||||
'.0:$firstHalfKey:0:$keyTwo',
|
||||
'.0:$secondHalfKey:0:0',
|
||||
'.0:$secondHalfKey:0:$keyFour',
|
||||
'.0:$keyFive:$keyFiveInner',
|
||||
'.0:$firstHalfKey//=1$keyZero/.$giraffe',
|
||||
'.0:$firstHalfKey//=1$keyTwo/.0',
|
||||
'.0:3/.0',
|
||||
'.0:$secondHalfKey//=1$keyFour/.$keyFour',
|
||||
'.0:$keyFive//=1$keyFiveInner/.0',
|
||||
]);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(zero, 0);
|
||||
expect(nthChild(mappedChildren, 0)).toBe(zeroMapped);
|
||||
expect(callback).toHaveBeenCalledWith(frag[0], 0);
|
||||
expect(mappedChildren[0]).toEqual(<div key=".0:$firstHalfKey//=1$keyZero/.$giraffe" />);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(one, 1);
|
||||
expect(nthChild(mappedChildren, 1)).toBe(oneMapped);
|
||||
expect(callback).toHaveBeenCalledWith(frag[1], 1);
|
||||
expect(mappedChildren[1]).toEqual(<div key=".0:$firstHalfKey//=1$keyTwo/.0" />);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(two, 2);
|
||||
expect(nthChild(mappedChildren, 2)).toBe(twoMapped);
|
||||
expect(callback).toHaveBeenCalledWith(frag[2], 2);
|
||||
expect(mappedChildren[2]).toEqual(<span key=".0:3/.0" />);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(three, 3);
|
||||
expect(nthChild(mappedChildren, 3)).toBe(threeMapped);
|
||||
expect(callback).toHaveBeenCalledWith(frag[3], 3);
|
||||
expect(mappedChildren[3]).toEqual(<div key=".0:$secondHalfKey//=1$keyFour/.$keyFour" />);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(four, 4);
|
||||
expect(nthChild(mappedChildren, 4)).toBe(fourMapped);
|
||||
|
||||
expect(callback).toHaveBeenCalledWith(five, 5);
|
||||
expect(nthChild(mappedChildren, 5)).toBe(fiveMapped);
|
||||
expect(callback).toHaveBeenCalledWith(frag[4], 4);
|
||||
expect(mappedChildren[4]).toEqual(<div key=".0:$keyFive//=1$keyFiveInner/.0" />);
|
||||
});
|
||||
|
||||
it('should retain key across two mappings', function() {
|
||||
@@ -302,20 +273,20 @@ describe('ReactChildren', function() {
|
||||
</div>
|
||||
);
|
||||
|
||||
var expectedForcedKeys = ['.$keyZero', '.$keyOne'];
|
||||
var expectedForcedKeys = ['.$keyZero/.$giraffe', '.$keyOne/.0'];
|
||||
var mappedChildrenForcedKeys =
|
||||
ReactChildren.map(forcedKeys.props.children, mapFn);
|
||||
var mappedForcedKeys = Object.keys(mappedChildrenForcedKeys);
|
||||
var mappedForcedKeys = mappedChildrenForcedKeys.map((c) => c.key);
|
||||
expect(mappedForcedKeys).toEqual(expectedForcedKeys);
|
||||
|
||||
var expectedRemappedForcedKeys = [
|
||||
'.$=1$keyZero:$giraffe',
|
||||
'.$=1$keyOne:0',
|
||||
'.$=1$keyZero//=1$giraffe/.$giraffe',
|
||||
'.$=1$keyOne//=10/.0',
|
||||
];
|
||||
var remappedChildrenForcedKeys =
|
||||
ReactChildren.map(mappedChildrenForcedKeys, mapFn);
|
||||
expect(
|
||||
Object.keys(remappedChildrenForcedKeys)
|
||||
remappedChildrenForcedKeys.map((c) => c.key)
|
||||
).toEqual(expectedRemappedForcedKeys);
|
||||
|
||||
});
|
||||
@@ -355,8 +326,7 @@ describe('ReactChildren', function() {
|
||||
var mapped = ReactChildren.map(instance.props.children, mapFn);
|
||||
|
||||
expect(console.error.calls.length).toEqual(1);
|
||||
expect(nthChild(mapped, 0)).toBe(zero);
|
||||
expect(keyOfNthChild(mapped, 0)).toBe('.$something');
|
||||
expect(mapped[0]).toEqual(<div key=".$something/.$something" />);
|
||||
});
|
||||
|
||||
it('should return 0 for null children', function() {
|
||||
@@ -424,40 +394,6 @@ describe('ReactChildren', function() {
|
||||
expect(numberOfChildren).toBe(6);
|
||||
});
|
||||
|
||||
it('should warn if a fragment is used without the wrapper', function() {
|
||||
spyOn(console, 'error');
|
||||
var child = React.createElement('span');
|
||||
ReactChildren.forEach({a: child, b: child}, function(c) {
|
||||
expect(c).toBe(child);
|
||||
});
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toContain('use of a keyed object');
|
||||
});
|
||||
|
||||
it('should warn if a fragment is accessed', function() {
|
||||
spyOn(console, 'error');
|
||||
var child = React.createElement('span');
|
||||
var frag = ReactChildren.map([child, child], function(c) {
|
||||
return c;
|
||||
});
|
||||
for (var key in frag) {
|
||||
void frag[key];
|
||||
break;
|
||||
}
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toContain('is an opaque type');
|
||||
|
||||
var frag2 = ReactChildren.map([child, child], function(c) {
|
||||
return c;
|
||||
});
|
||||
for (var key2 in frag2) {
|
||||
frag2[key2] = 123;
|
||||
break;
|
||||
}
|
||||
expect(console.error.calls.length).toBe(2);
|
||||
expect(console.error.calls[1].args[0]).toContain('is an immutable opaque');
|
||||
});
|
||||
|
||||
it('should flatten children to an array', function() {
|
||||
expect(ReactChildren.toArray(undefined)).toEqual([]);
|
||||
expect(ReactChildren.toArray(null)).toEqual([]);
|
||||
|
||||
@@ -14,22 +14,15 @@
|
||||
describe('sliceChildren', function() {
|
||||
|
||||
var React;
|
||||
var ReactFragment;
|
||||
|
||||
var sliceChildren;
|
||||
|
||||
beforeEach(function() {
|
||||
React = require('React');
|
||||
ReactFragment = require('ReactFragment');
|
||||
|
||||
sliceChildren = require('sliceChildren');
|
||||
});
|
||||
|
||||
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 = [
|
||||
<div key="A" />,
|
||||
@@ -37,11 +30,11 @@ describe('sliceChildren', function() {
|
||||
<div key="C" />,
|
||||
];
|
||||
var children = sliceChildren(fullSet, 0);
|
||||
testKeyValuePairs(children, {
|
||||
'.$A': fullSet[0],
|
||||
'.$B': fullSet[1],
|
||||
'.$C': fullSet[2],
|
||||
});
|
||||
expect(children).toEqual([
|
||||
<div key=".$A" />,
|
||||
<div key=".$B" />,
|
||||
<div key=".$C" />,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should render the remaining set if no end index is supplied', function() {
|
||||
@@ -51,10 +44,10 @@ describe('sliceChildren', function() {
|
||||
<div key="C" />,
|
||||
];
|
||||
var children = sliceChildren(fullSet, 1);
|
||||
testKeyValuePairs(children, {
|
||||
'.$B': fullSet[1],
|
||||
'.$C': fullSet[2],
|
||||
});
|
||||
expect(children).toEqual([
|
||||
<div key=".$B" />,
|
||||
<div key=".$C" />,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should exclude everything at or after the end index', function() {
|
||||
@@ -65,21 +58,36 @@ describe('sliceChildren', function() {
|
||||
<div key="D" />,
|
||||
];
|
||||
var children = sliceChildren(fullSet, 1, 2);
|
||||
testKeyValuePairs(children, {
|
||||
'.$B': fullSet[1],
|
||||
});
|
||||
expect(children).toEqual([
|
||||
<div key=".$B" />,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should allow static children to be sliced', function() {
|
||||
var a = <div />;
|
||||
var b = <div />;
|
||||
var c = <div />;
|
||||
var a = <a />;
|
||||
var b = <b />;
|
||||
var c = <i />;
|
||||
|
||||
var el = <div>{a}{b}{c}</div>;
|
||||
var children = sliceChildren(el.props.children, 1, 2);
|
||||
testKeyValuePairs(children, {
|
||||
'.1': b,
|
||||
});
|
||||
expect(children).toEqual([
|
||||
<b key=".1" />,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should slice nested children', function() {
|
||||
var fullSet = [
|
||||
<div key="A" />,
|
||||
[
|
||||
<div key="B" />,
|
||||
<div key="C" />,
|
||||
],
|
||||
<div key="D" />,
|
||||
];
|
||||
var children = sliceChildren(fullSet, 1, 2);
|
||||
expect(children).toEqual([
|
||||
<div key=".1:$B" />,
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactFragment = require('ReactFragment');
|
||||
|
||||
var flattenChildren = require('flattenChildren');
|
||||
var ReactChildren = require('ReactChildren');
|
||||
|
||||
/**
|
||||
* Slice children that are typically specified as `props.children`. This version
|
||||
@@ -29,23 +27,8 @@ function sliceChildren(children, start, end) {
|
||||
return children;
|
||||
}
|
||||
|
||||
var slicedChildren = {};
|
||||
var flattenedMap = flattenChildren(children);
|
||||
var ii = 0;
|
||||
for (var key in flattenedMap) {
|
||||
if (!flattenedMap.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
var child = flattenedMap[key];
|
||||
if (ii >= start) {
|
||||
slicedChildren[key] = child;
|
||||
}
|
||||
ii++;
|
||||
if (end != null && ii >= end) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ReactFragment.create(slicedChildren);
|
||||
var array = ReactChildren.toArray(children);
|
||||
return array.slice(start, end);
|
||||
}
|
||||
|
||||
module.exports = sliceChildren;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
'use strict';
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactFragment = require('ReactFragment');
|
||||
var ReactPropTypeLocations = require('ReactPropTypeLocations');
|
||||
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
@@ -47,8 +46,6 @@ var ownerHasKeyUseWarning = {};
|
||||
|
||||
var loggedTypeFailures = {};
|
||||
|
||||
var NUMERIC_PROPERTY_REGEX = /^\d+$/;
|
||||
|
||||
/**
|
||||
* Warn if the element doesn't have an explicit key assigned to it.
|
||||
* This element is in an array. The array could grow and shrink or be
|
||||
@@ -80,34 +77,6 @@ function validateExplicitKey(element, parentType) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn if the key is being defined as an object property but has an incorrect
|
||||
* value.
|
||||
*
|
||||
* @internal
|
||||
* @param {string} name Property name of the key.
|
||||
* @param {ReactElement} element Component that requires a key.
|
||||
* @param {*} parentType element's parent's type.
|
||||
*/
|
||||
function validatePropertyKey(name, element, parentType) {
|
||||
if (!NUMERIC_PROPERTY_REGEX.test(name)) {
|
||||
return;
|
||||
}
|
||||
var addenda = getAddendaForKeyUse('numericKeys', element, parentType);
|
||||
if (addenda === null) {
|
||||
// we already showed the warning
|
||||
return;
|
||||
}
|
||||
warning(
|
||||
false,
|
||||
'Child objects should have non-numeric keys so ordering is preserved.' +
|
||||
'%s%s%s',
|
||||
addenda.parentOrOwner || '',
|
||||
addenda.childOwner || '',
|
||||
addenda.url || ''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared warning and monitoring code for the key warnings.
|
||||
*
|
||||
@@ -189,13 +158,6 @@ function validateChildKeys(node, parentType) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (typeof node === 'object') {
|
||||
var fragment = ReactFragment.extractIfFragment(node);
|
||||
for (var key in fragment) {
|
||||
if (fragment.hasOwnProperty(key)) {
|
||||
validatePropertyKey(key, fragment[key], parentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactFragment;
|
||||
var ReactTestUtils;
|
||||
|
||||
describe('ReactElementValidator', function() {
|
||||
@@ -27,7 +26,6 @@ describe('ReactElementValidator', function() {
|
||||
|
||||
React = require('React');
|
||||
ReactDOM = require('ReactDOM');
|
||||
ReactFragment = require('ReactFragment');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
ComponentClass = React.createClass({
|
||||
render: function() {
|
||||
@@ -36,10 +34,6 @@ describe('ReactElementValidator', function() {
|
||||
});
|
||||
});
|
||||
|
||||
function frag(obj) {
|
||||
return ReactFragment.create(obj);
|
||||
}
|
||||
|
||||
it('warns for keys for arrays of elements in rest args', function() {
|
||||
spyOn(console, 'error');
|
||||
var Component = React.createFactory(ComponentClass);
|
||||
@@ -205,41 +199,6 @@ describe('ReactElementValidator', function() {
|
||||
expect(console.error.argsForCall.length).toBe(0);
|
||||
});
|
||||
|
||||
it('warns for numeric keys on objects in rest args', function() {
|
||||
spyOn(console, 'error');
|
||||
var Component = React.createFactory(ComponentClass);
|
||||
|
||||
Component(null, frag({1: Component(), 2: Component()}));
|
||||
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'Child objects should have non-numeric keys so ordering is preserved.'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not warn for numeric keys in entry iterables in rest args',
|
||||
function() {
|
||||
spyOn(console, 'error');
|
||||
var Component = React.createFactory(ComponentClass);
|
||||
|
||||
var iterable = {
|
||||
'@@iterator': function() {
|
||||
var i = 0;
|
||||
return {
|
||||
next: function() {
|
||||
var done = ++i > 2;
|
||||
return {value: done ? undefined : [i, Component()], done: done};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
iterable.entries = iterable['@@iterator'];
|
||||
|
||||
Component(null, iterable);
|
||||
|
||||
expect(console.error.argsForCall.length).toBe(0);
|
||||
});
|
||||
|
||||
it('does not warn when the element is directly in rest args', function() {
|
||||
spyOn(console, 'error');
|
||||
var Component = React.createFactory(ComponentClass);
|
||||
@@ -454,14 +413,6 @@ describe('ReactElementValidator', function() {
|
||||
);
|
||||
});
|
||||
|
||||
it('should warn if a fragment is used without the wrapper', function() {
|
||||
spyOn(console, 'error');
|
||||
var child = React.createElement('span');
|
||||
React.createElement('div', null, {a: child, b: child});
|
||||
expect(console.error.calls.length).toBe(1);
|
||||
expect(console.error.calls[0].args[0]).toContain('use of a keyed object');
|
||||
});
|
||||
|
||||
it('should warn when accessing .type on an element factory', function() {
|
||||
spyOn(console, 'error');
|
||||
var TestComponent = React.createClass({
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
'use strict';
|
||||
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactFragment = require('ReactFragment');
|
||||
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
|
||||
|
||||
var emptyFunction = require('emptyFunction');
|
||||
@@ -371,12 +370,7 @@ function isNode(propValue) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
propValue = ReactFragment.extractIfFragment(propValue);
|
||||
for (var k in propValue) {
|
||||
if (!isNode(propValue[k])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -368,6 +368,7 @@ describe('ReactPropTypes', function() {
|
||||
typeCheckFail(PropTypes.node, true, failMessage);
|
||||
typeCheckFail(PropTypes.node, function() {}, failMessage);
|
||||
typeCheckFail(PropTypes.node, {key: function() {}}, failMessage);
|
||||
typeCheckFail(PropTypes.node, {key: <div />}, failMessage);
|
||||
});
|
||||
|
||||
it('should not warn for valid values', function() {
|
||||
@@ -377,7 +378,6 @@ describe('ReactPropTypes', function() {
|
||||
typeCheckPass(PropTypes.node, <MyComponent />);
|
||||
typeCheckPass(PropTypes.node, 'Some string');
|
||||
typeCheckPass(PropTypes.node, []);
|
||||
typeCheckPass(PropTypes.node, {});
|
||||
|
||||
typeCheckPass(PropTypes.node, [
|
||||
123,
|
||||
@@ -402,20 +402,6 @@ describe('ReactPropTypes', function() {
|
||||
k5: undefined,
|
||||
}));
|
||||
expect(console.error.calls).toEqual([]);
|
||||
|
||||
// This should also pass, though it warns
|
||||
typeCheckPass(PropTypes.node, {
|
||||
k0: 123,
|
||||
k1: 'Some string',
|
||||
k2: <div />,
|
||||
k3: {
|
||||
k30: <MyComponent />,
|
||||
k31: {k310: <a />},
|
||||
k32: 'Another string',
|
||||
},
|
||||
k4: null,
|
||||
k5: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not warn for iterables', function() {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
// of dynamic errors when using JSX with Flow.
|
||||
|
||||
var React;
|
||||
var ReactFragment;
|
||||
var ReactTestUtils;
|
||||
|
||||
describe('ReactJSXElementValidator', function() {
|
||||
@@ -26,7 +25,6 @@ describe('ReactJSXElementValidator', function() {
|
||||
require('mock-modules').dumpCache();
|
||||
|
||||
React = require('React');
|
||||
ReactFragment = require('ReactFragment');
|
||||
ReactTestUtils = require('ReactTestUtils');
|
||||
|
||||
Component = class {
|
||||
@@ -44,10 +42,6 @@ describe('ReactJSXElementValidator', function() {
|
||||
RequiredPropComponent.propTypes = {prop: React.PropTypes.string.isRequired};
|
||||
});
|
||||
|
||||
function frag(obj) {
|
||||
return ReactFragment.create(obj);
|
||||
}
|
||||
|
||||
it('warns for keys for arrays of elements in children position', function() {
|
||||
spyOn(console, 'error');
|
||||
|
||||
@@ -142,17 +136,6 @@ describe('ReactJSXElementValidator', function() {
|
||||
expect(console.error.argsForCall.length).toBe(0);
|
||||
});
|
||||
|
||||
it('warns for numeric keys on objects as children', function() {
|
||||
spyOn(console, 'error');
|
||||
|
||||
void <Component>{frag({1: <Component />, 2: <Component />})}</Component>;
|
||||
|
||||
expect(console.error.argsForCall.length).toBe(1);
|
||||
expect(console.error.argsForCall[0][0]).toContain(
|
||||
'Child objects should have non-numeric keys so ordering is preserved.'
|
||||
);
|
||||
});
|
||||
|
||||
it('does not warn for numeric keys in entry iterable as a child', function() {
|
||||
spyOn(console, 'error');
|
||||
|
||||
|
||||
@@ -53,8 +53,8 @@ describe('ReactIdentity', function() {
|
||||
instance = ReactDOM.render(instance, document.createElement('div'));
|
||||
var node = ReactDOM.findDOMNode(instance);
|
||||
expect(node.childNodes.length).toBe(2);
|
||||
checkID(node.childNodes[0], '.0.$first:0');
|
||||
checkID(node.childNodes[1], '.0.$second:0');
|
||||
checkID(node.childNodes[0], '.0.$first/=10');
|
||||
checkID(node.childNodes[1], '.0.$second/=10');
|
||||
});
|
||||
|
||||
it('should allow key property to express identity', function() {
|
||||
@@ -125,9 +125,9 @@ describe('ReactIdentity', function() {
|
||||
expect(ReactDOM.findDOMNode(span2)).not.toBe(null);
|
||||
|
||||
key = key.replace(/=/g, '=0');
|
||||
|
||||
checkID(ReactDOM.findDOMNode(span1), '.0.$' + key);
|
||||
checkID(ReactDOM.findDOMNode(span2), '.0.1:$' + key + ':0');
|
||||
key = key.replace(/\//g, '//');
|
||||
checkID(ReactDOM.findDOMNode(span2), '.0.1:$' + key + '/=10');
|
||||
}
|
||||
|
||||
it('should allow any character as a key, in a detached parent', function() {
|
||||
|
||||
@@ -162,10 +162,10 @@ describe('traverseAllChildren', function() {
|
||||
traverseContext, div, '.$divNode'
|
||||
);
|
||||
expect(traverseFn).toHaveBeenCalledWith(
|
||||
traverseContext, span, '.1:0:$span:$spanNode'
|
||||
traverseContext, <span key="span/.$spanNode" />, '.1:0:$span/=1$spanNode'
|
||||
);
|
||||
expect(traverseFn).toHaveBeenCalledWith(
|
||||
traverseContext, a, '.2:$a:$aNode'
|
||||
traverseContext, <a key="a/.$aNode" />, '.2:$a/=1$aNode'
|
||||
);
|
||||
expect(traverseFn).toHaveBeenCalledWith(
|
||||
traverseContext, 'string', '.3'
|
||||
@@ -224,35 +224,26 @@ describe('traverseAllChildren', function() {
|
||||
expect(traverseContext.length).toEqual(6);
|
||||
expect(traverseFn).toHaveBeenCalledWith(
|
||||
traverseContext,
|
||||
zero,
|
||||
'.0:$firstHalfKey:0:$keyZero'
|
||||
);
|
||||
|
||||
expect(traverseFn)
|
||||
.toHaveBeenCalledWith(traverseContext, one, '.0:$firstHalfKey:0:1');
|
||||
|
||||
expect(traverseFn).toHaveBeenCalledWith(
|
||||
traverseContext,
|
||||
two,
|
||||
'.0:$firstHalfKey:0:$keyTwo'
|
||||
<div key="firstHalfKey/.$keyZero" />,
|
||||
'.0:$firstHalfKey/=1$keyZero'
|
||||
);
|
||||
|
||||
expect(traverseFn).toHaveBeenCalledWith(
|
||||
traverseContext,
|
||||
three,
|
||||
'.0:$secondHalfKey:0:0'
|
||||
<div key="firstHalfKey/.$keyTwo" />,
|
||||
'.0:$firstHalfKey/=1$keyTwo'
|
||||
);
|
||||
|
||||
expect(traverseFn).toHaveBeenCalledWith(
|
||||
traverseContext,
|
||||
four,
|
||||
'.0:$secondHalfKey:0:$keyFour'
|
||||
<div key="secondHalfKey/.$keyFour" />,
|
||||
'.0:$secondHalfKey/=1$keyFour'
|
||||
);
|
||||
|
||||
expect(traverseFn).toHaveBeenCalledWith(
|
||||
traverseContext,
|
||||
five,
|
||||
'.0:$keyFive:$keyFiveInner'
|
||||
<div key="keyFive/.$keyFiveInner" />,
|
||||
'.0:$keyFive/=1$keyFiveInner'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ReactCurrentOwner = require('ReactCurrentOwner');
|
||||
var ReactElement = require('ReactElement');
|
||||
var ReactFragment = require('ReactFragment');
|
||||
var ReactInstanceHandles = require('ReactInstanceHandles');
|
||||
|
||||
var getIteratorFn = require('getIteratorFn');
|
||||
@@ -179,28 +179,24 @@ function traverseAllChildrenImpl(
|
||||
}
|
||||
}
|
||||
} else if (type === 'object') {
|
||||
invariant(
|
||||
children.nodeType !== 1,
|
||||
'traverseAllChildren(...): Encountered an invalid child; DOM ' +
|
||||
'elements are not valid children of React components.'
|
||||
);
|
||||
var fragment = ReactFragment.extract(children);
|
||||
for (var key in fragment) {
|
||||
if (fragment.hasOwnProperty(key)) {
|
||||
child = fragment[key];
|
||||
nextName = (
|
||||
nextNamePrefix +
|
||||
wrapUserProvidedKey(key) + SUBSEPARATOR +
|
||||
getComponentKey(child, 0)
|
||||
);
|
||||
subtreeCount += traverseAllChildrenImpl(
|
||||
child,
|
||||
nextName,
|
||||
callback,
|
||||
traverseContext
|
||||
);
|
||||
var addendum = '';
|
||||
if (__DEV__) {
|
||||
if (ReactCurrentOwner.current) {
|
||||
var name = ReactCurrentOwner.current.getName();
|
||||
if (name) {
|
||||
addendum = ' Check the render method of `' + name + '`.'
|
||||
}
|
||||
}
|
||||
}
|
||||
invariant(
|
||||
false,
|
||||
'Objects are not valid as a React child (found object with keys ' +
|
||||
'{%s}). If you meant to render a collection of children, use an ' +
|
||||
'array instead or wrap the object using ' +
|
||||
'React.addons.createFragment(object).%s',
|
||||
Object.keys(children).join(', '),
|
||||
addendum
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user