You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
5.6 KiB
182 lines
5.6 KiB
10 years ago
|
/**
|
||
|
* 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 ("production" !== process.env.NODE_ENV) {
|
||
|
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() {
|
||
|
("production" !== process.env.NODE_ENV ? 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.'
|
||
|
) : null);
|
||
|
this[didWarnKey] = true;
|
||
|
return this[fragmentKey][key];
|
||
|
},
|
||
|
set: function(value) {
|
||
|
("production" !== process.env.NODE_ENV ? warning(
|
||
|
this[didWarnKey],
|
||
|
'A ReactFragment is an immutable opaque type. Mutating its ' +
|
||
|
'properties is deprecated.'
|
||
|
) : null);
|
||
|
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 ("production" !== process.env.NODE_ENV) {
|
||
|
if (typeof object !== 'object' || !object || Array.isArray(object)) {
|
||
|
("production" !== process.env.NODE_ENV ? warning(
|
||
|
false,
|
||
|
'React.addons.createFragment only accepts a single object.',
|
||
|
object
|
||
|
) : null);
|
||
|
return object;
|
||
|
}
|
||
|
if (ReactElement.isValidElement(object)) {
|
||
|
("production" !== process.env.NODE_ENV ? warning(
|
||
|
false,
|
||
|
'React.addons.createFragment does not accept a ReactElement ' +
|
||
|
'without a wrapper object.'
|
||
|
) : null);
|
||
|
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 ("production" !== process.env.NODE_ENV) {
|
||
|
if (canWarnForReactFragment) {
|
||
|
if (!fragment[fragmentKey]) {
|
||
|
("production" !== process.env.NODE_ENV ? warning(
|
||
|
didWarnForFragment(fragment),
|
||
|
'Any use of a keyed object should be wrapped in ' +
|
||
|
'React.addons.createFragment(object) before being passed as a ' +
|
||
|
'child.'
|
||
|
) : null);
|
||
|
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 ("production" !== process.env.NODE_ENV) {
|
||
|
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;
|