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.
338 lines
11 KiB
338 lines
11 KiB
/** |
|
* Copyright (c) 2014-present, Facebook, Inc. |
|
* |
|
* This source code is licensed under the MIT license found in the |
|
* LICENSE file in the root directory of this source tree. |
|
* |
|
*/ |
|
|
|
'use strict'; |
|
|
|
var _assign = require('object-assign'); |
|
|
|
var ReactCurrentOwner = require('./ReactCurrentOwner'); |
|
|
|
var warning = require('fbjs/lib/warning'); |
|
var canDefineProperty = require('./canDefineProperty'); |
|
var hasOwnProperty = Object.prototype.hasOwnProperty; |
|
|
|
var REACT_ELEMENT_TYPE = require('./ReactElementSymbol'); |
|
|
|
var RESERVED_PROPS = { |
|
key: true, |
|
ref: true, |
|
__self: true, |
|
__source: true |
|
}; |
|
|
|
var specialPropKeyWarningShown, specialPropRefWarningShown; |
|
|
|
function hasValidRef(config) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
if (hasOwnProperty.call(config, 'ref')) { |
|
var getter = Object.getOwnPropertyDescriptor(config, 'ref').get; |
|
if (getter && getter.isReactWarning) { |
|
return false; |
|
} |
|
} |
|
} |
|
return config.ref !== undefined; |
|
} |
|
|
|
function hasValidKey(config) { |
|
if (process.env.NODE_ENV !== 'production') { |
|
if (hasOwnProperty.call(config, 'key')) { |
|
var getter = Object.getOwnPropertyDescriptor(config, 'key').get; |
|
if (getter && getter.isReactWarning) { |
|
return false; |
|
} |
|
} |
|
} |
|
return config.key !== undefined; |
|
} |
|
|
|
function defineKeyPropWarningGetter(props, displayName) { |
|
var warnAboutAccessingKey = function () { |
|
if (!specialPropKeyWarningShown) { |
|
specialPropKeyWarningShown = true; |
|
process.env.NODE_ENV !== 'production' ? warning(false, '%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName) : void 0; |
|
} |
|
}; |
|
warnAboutAccessingKey.isReactWarning = true; |
|
Object.defineProperty(props, 'key', { |
|
get: warnAboutAccessingKey, |
|
configurable: true |
|
}); |
|
} |
|
|
|
function defineRefPropWarningGetter(props, displayName) { |
|
var warnAboutAccessingRef = function () { |
|
if (!specialPropRefWarningShown) { |
|
specialPropRefWarningShown = true; |
|
process.env.NODE_ENV !== 'production' ? warning(false, '%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://fb.me/react-special-props)', displayName) : void 0; |
|
} |
|
}; |
|
warnAboutAccessingRef.isReactWarning = true; |
|
Object.defineProperty(props, 'ref', { |
|
get: warnAboutAccessingRef, |
|
configurable: true |
|
}); |
|
} |
|
|
|
/** |
|
* Factory method to create a new React element. This no longer adheres to |
|
* the class pattern, so do not use new to call it. Also, no instanceof check |
|
* will work. Instead test $$typeof field against Symbol.for('react.element') to check |
|
* if something is a React Element. |
|
* |
|
* @param {*} type |
|
* @param {*} key |
|
* @param {string|object} ref |
|
* @param {*} self A *temporary* helper to detect places where `this` is |
|
* different from the `owner` when React.createElement is called, so that we |
|
* can warn. We want to get rid of owner and replace string `ref`s with arrow |
|
* functions, and as long as `this` and owner are the same, there will be no |
|
* change in behavior. |
|
* @param {*} source An annotation object (added by a transpiler or otherwise) |
|
* indicating filename, line number, and/or other information. |
|
* @param {*} owner |
|
* @param {*} props |
|
* @internal |
|
*/ |
|
var ReactElement = function (type, key, ref, self, source, owner, props) { |
|
var element = { |
|
// This tag allow us to uniquely identify this as a React Element |
|
$$typeof: REACT_ELEMENT_TYPE, |
|
|
|
// Built-in properties that belong on the element |
|
type: type, |
|
key: key, |
|
ref: ref, |
|
props: props, |
|
|
|
// Record the component responsible for creating this element. |
|
_owner: owner |
|
}; |
|
|
|
if (process.env.NODE_ENV !== 'production') { |
|
// The validation flag is currently mutative. We put it on |
|
// an external backing store so that we can freeze the whole object. |
|
// This can be replaced with a WeakMap once they are implemented in |
|
// commonly used development environments. |
|
element._store = {}; |
|
|
|
// To make comparing ReactElements easier for testing purposes, we make |
|
// the validation flag non-enumerable (where possible, which should |
|
// include every environment we run tests in), so the test framework |
|
// ignores it. |
|
if (canDefineProperty) { |
|
Object.defineProperty(element._store, 'validated', { |
|
configurable: false, |
|
enumerable: false, |
|
writable: true, |
|
value: false |
|
}); |
|
// self and source are DEV only properties. |
|
Object.defineProperty(element, '_self', { |
|
configurable: false, |
|
enumerable: false, |
|
writable: false, |
|
value: self |
|
}); |
|
// Two elements created in two different places should be considered |
|
// equal for testing purposes and therefore we hide it from enumeration. |
|
Object.defineProperty(element, '_source', { |
|
configurable: false, |
|
enumerable: false, |
|
writable: false, |
|
value: source |
|
}); |
|
} else { |
|
element._store.validated = false; |
|
element._self = self; |
|
element._source = source; |
|
} |
|
if (Object.freeze) { |
|
Object.freeze(element.props); |
|
Object.freeze(element); |
|
} |
|
} |
|
|
|
return element; |
|
}; |
|
|
|
/** |
|
* Create and return a new ReactElement of the given type. |
|
* See https://facebook.github.io/react/docs/top-level-api.html#react.createelement |
|
*/ |
|
ReactElement.createElement = function (type, config, children) { |
|
var propName; |
|
|
|
// Reserved names are extracted |
|
var props = {}; |
|
|
|
var key = null; |
|
var ref = null; |
|
var self = null; |
|
var source = null; |
|
|
|
if (config != null) { |
|
if (hasValidRef(config)) { |
|
ref = config.ref; |
|
} |
|
if (hasValidKey(config)) { |
|
key = '' + config.key; |
|
} |
|
|
|
self = config.__self === undefined ? null : config.__self; |
|
source = config.__source === undefined ? null : config.__source; |
|
// Remaining properties are added to a new props object |
|
for (propName in config) { |
|
if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { |
|
props[propName] = config[propName]; |
|
} |
|
} |
|
} |
|
|
|
// Children can be more than one argument, and those are transferred onto |
|
// the newly allocated props object. |
|
var childrenLength = arguments.length - 2; |
|
if (childrenLength === 1) { |
|
props.children = children; |
|
} else if (childrenLength > 1) { |
|
var childArray = Array(childrenLength); |
|
for (var i = 0; i < childrenLength; i++) { |
|
childArray[i] = arguments[i + 2]; |
|
} |
|
if (process.env.NODE_ENV !== 'production') { |
|
if (Object.freeze) { |
|
Object.freeze(childArray); |
|
} |
|
} |
|
props.children = childArray; |
|
} |
|
|
|
// Resolve default props |
|
if (type && type.defaultProps) { |
|
var defaultProps = type.defaultProps; |
|
for (propName in defaultProps) { |
|
if (props[propName] === undefined) { |
|
props[propName] = defaultProps[propName]; |
|
} |
|
} |
|
} |
|
if (process.env.NODE_ENV !== 'production') { |
|
if (key || ref) { |
|
if (typeof props.$$typeof === 'undefined' || props.$$typeof !== REACT_ELEMENT_TYPE) { |
|
var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type; |
|
if (key) { |
|
defineKeyPropWarningGetter(props, displayName); |
|
} |
|
if (ref) { |
|
defineRefPropWarningGetter(props, displayName); |
|
} |
|
} |
|
} |
|
} |
|
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props); |
|
}; |
|
|
|
/** |
|
* Return a function that produces ReactElements of a given type. |
|
* See https://facebook.github.io/react/docs/top-level-api.html#react.createfactory |
|
*/ |
|
ReactElement.createFactory = function (type) { |
|
var factory = ReactElement.createElement.bind(null, type); |
|
// Expose the type on the factory and the prototype so that it can be |
|
// easily accessed on elements. E.g. `<Foo />.type === Foo`. |
|
// This should not be named `constructor` since this may not be the function |
|
// that created the element, and it may not even be a constructor. |
|
// Legacy hook TODO: Warn if this is accessed |
|
factory.type = type; |
|
return factory; |
|
}; |
|
|
|
ReactElement.cloneAndReplaceKey = function (oldElement, newKey) { |
|
var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props); |
|
|
|
return newElement; |
|
}; |
|
|
|
/** |
|
* Clone and return a new ReactElement using element as the starting point. |
|
* See https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement |
|
*/ |
|
ReactElement.cloneElement = function (element, config, children) { |
|
var propName; |
|
|
|
// Original props are copied |
|
var props = _assign({}, element.props); |
|
|
|
// Reserved names are extracted |
|
var key = element.key; |
|
var ref = element.ref; |
|
// Self is preserved since the owner is preserved. |
|
var self = element._self; |
|
// Source is preserved since cloneElement is unlikely to be targeted by a |
|
// transpiler, and the original source is probably a better indicator of the |
|
// true owner. |
|
var source = element._source; |
|
|
|
// Owner will be preserved, unless ref is overridden |
|
var owner = element._owner; |
|
|
|
if (config != null) { |
|
if (hasValidRef(config)) { |
|
// Silently steal the ref from the parent. |
|
ref = config.ref; |
|
owner = ReactCurrentOwner.current; |
|
} |
|
if (hasValidKey(config)) { |
|
key = '' + config.key; |
|
} |
|
|
|
// Remaining properties override existing props |
|
var defaultProps; |
|
if (element.type && element.type.defaultProps) { |
|
defaultProps = element.type.defaultProps; |
|
} |
|
for (propName in config) { |
|
if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) { |
|
if (config[propName] === undefined && defaultProps !== undefined) { |
|
// Resolve default props |
|
props[propName] = defaultProps[propName]; |
|
} else { |
|
props[propName] = config[propName]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Children can be more than one argument, and those are transferred onto |
|
// the newly allocated props object. |
|
var childrenLength = arguments.length - 2; |
|
if (childrenLength === 1) { |
|
props.children = children; |
|
} else if (childrenLength > 1) { |
|
var childArray = Array(childrenLength); |
|
for (var i = 0; i < childrenLength; i++) { |
|
childArray[i] = arguments[i + 2]; |
|
} |
|
props.children = childArray; |
|
} |
|
|
|
return ReactElement(element.type, key, ref, self, source, owner, props); |
|
}; |
|
|
|
/** |
|
* Verifies the object is a ReactElement. |
|
* See https://facebook.github.io/react/docs/top-level-api.html#react.isvalidelement |
|
* @param {?object} object |
|
* @return {boolean} True if `object` is a valid component. |
|
* @final |
|
*/ |
|
ReactElement.isValidElement = function (object) { |
|
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE; |
|
}; |
|
|
|
module.exports = ReactElement; |