/** * 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;