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.
181 lines
5.6 KiB
181 lines
5.6 KiB
/** |
|
* 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;
|
|
|