UNPKG

11.1 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2014-present, Facebook, Inc.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9'use strict';
10
11var _assign = require('object-assign');
12
13var ReactCurrentOwner = require('./ReactCurrentOwner');
14
15var warning = require('fbjs/lib/warning');
16var canDefineProperty = require('./canDefineProperty');
17var hasOwnProperty = Object.prototype.hasOwnProperty;
18
19var REACT_ELEMENT_TYPE = require('./ReactElementSymbol');
20
21var RESERVED_PROPS = {
22 key: true,
23 ref: true,
24 __self: true,
25 __source: true
26};
27
28var specialPropKeyWarningShown, specialPropRefWarningShown;
29
30function hasValidRef(config) {
31 if (process.env.NODE_ENV !== 'production') {
32 if (hasOwnProperty.call(config, 'ref')) {
33 var getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
34 if (getter && getter.isReactWarning) {
35 return false;
36 }
37 }
38 }
39 return config.ref !== undefined;
40}
41
42function hasValidKey(config) {
43 if (process.env.NODE_ENV !== 'production') {
44 if (hasOwnProperty.call(config, 'key')) {
45 var getter = Object.getOwnPropertyDescriptor(config, 'key').get;
46 if (getter && getter.isReactWarning) {
47 return false;
48 }
49 }
50 }
51 return config.key !== undefined;
52}
53
54function defineKeyPropWarningGetter(props, displayName) {
55 var warnAboutAccessingKey = function () {
56 if (!specialPropKeyWarningShown) {
57 specialPropKeyWarningShown = true;
58 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;
59 }
60 };
61 warnAboutAccessingKey.isReactWarning = true;
62 Object.defineProperty(props, 'key', {
63 get: warnAboutAccessingKey,
64 configurable: true
65 });
66}
67
68function defineRefPropWarningGetter(props, displayName) {
69 var warnAboutAccessingRef = function () {
70 if (!specialPropRefWarningShown) {
71 specialPropRefWarningShown = true;
72 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;
73 }
74 };
75 warnAboutAccessingRef.isReactWarning = true;
76 Object.defineProperty(props, 'ref', {
77 get: warnAboutAccessingRef,
78 configurable: true
79 });
80}
81
82/**
83 * Factory method to create a new React element. This no longer adheres to
84 * the class pattern, so do not use new to call it. Also, no instanceof check
85 * will work. Instead test $$typeof field against Symbol.for('react.element') to check
86 * if something is a React Element.
87 *
88 * @param {*} type
89 * @param {*} key
90 * @param {string|object} ref
91 * @param {*} self A *temporary* helper to detect places where `this` is
92 * different from the `owner` when React.createElement is called, so that we
93 * can warn. We want to get rid of owner and replace string `ref`s with arrow
94 * functions, and as long as `this` and owner are the same, there will be no
95 * change in behavior.
96 * @param {*} source An annotation object (added by a transpiler or otherwise)
97 * indicating filename, line number, and/or other information.
98 * @param {*} owner
99 * @param {*} props
100 * @internal
101 */
102var ReactElement = function (type, key, ref, self, source, owner, props) {
103 var element = {
104 // This tag allow us to uniquely identify this as a React Element
105 $$typeof: REACT_ELEMENT_TYPE,
106
107 // Built-in properties that belong on the element
108 type: type,
109 key: key,
110 ref: ref,
111 props: props,
112
113 // Record the component responsible for creating this element.
114 _owner: owner
115 };
116
117 if (process.env.NODE_ENV !== 'production') {
118 // The validation flag is currently mutative. We put it on
119 // an external backing store so that we can freeze the whole object.
120 // This can be replaced with a WeakMap once they are implemented in
121 // commonly used development environments.
122 element._store = {};
123
124 // To make comparing ReactElements easier for testing purposes, we make
125 // the validation flag non-enumerable (where possible, which should
126 // include every environment we run tests in), so the test framework
127 // ignores it.
128 if (canDefineProperty) {
129 Object.defineProperty(element._store, 'validated', {
130 configurable: false,
131 enumerable: false,
132 writable: true,
133 value: false
134 });
135 // self and source are DEV only properties.
136 Object.defineProperty(element, '_self', {
137 configurable: false,
138 enumerable: false,
139 writable: false,
140 value: self
141 });
142 // Two elements created in two different places should be considered
143 // equal for testing purposes and therefore we hide it from enumeration.
144 Object.defineProperty(element, '_source', {
145 configurable: false,
146 enumerable: false,
147 writable: false,
148 value: source
149 });
150 } else {
151 element._store.validated = false;
152 element._self = self;
153 element._source = source;
154 }
155 if (Object.freeze) {
156 Object.freeze(element.props);
157 Object.freeze(element);
158 }
159 }
160
161 return element;
162};
163
164/**
165 * Create and return a new ReactElement of the given type.
166 * See https://facebook.github.io/react/docs/top-level-api.html#react.createelement
167 */
168ReactElement.createElement = function (type, config, children) {
169 var propName;
170
171 // Reserved names are extracted
172 var props = {};
173
174 var key = null;
175 var ref = null;
176 var self = null;
177 var source = null;
178
179 if (config != null) {
180 if (hasValidRef(config)) {
181 ref = config.ref;
182 }
183 if (hasValidKey(config)) {
184 key = '' + config.key;
185 }
186
187 self = config.__self === undefined ? null : config.__self;
188 source = config.__source === undefined ? null : config.__source;
189 // Remaining properties are added to a new props object
190 for (propName in config) {
191 if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
192 props[propName] = config[propName];
193 }
194 }
195 }
196
197 // Children can be more than one argument, and those are transferred onto
198 // the newly allocated props object.
199 var childrenLength = arguments.length - 2;
200 if (childrenLength === 1) {
201 props.children = children;
202 } else if (childrenLength > 1) {
203 var childArray = Array(childrenLength);
204 for (var i = 0; i < childrenLength; i++) {
205 childArray[i] = arguments[i + 2];
206 }
207 if (process.env.NODE_ENV !== 'production') {
208 if (Object.freeze) {
209 Object.freeze(childArray);
210 }
211 }
212 props.children = childArray;
213 }
214
215 // Resolve default props
216 if (type && type.defaultProps) {
217 var defaultProps = type.defaultProps;
218 for (propName in defaultProps) {
219 if (props[propName] === undefined) {
220 props[propName] = defaultProps[propName];
221 }
222 }
223 }
224 if (process.env.NODE_ENV !== 'production') {
225 if (key || ref) {
226 if (typeof props.$$typeof === 'undefined' || props.$$typeof !== REACT_ELEMENT_TYPE) {
227 var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
228 if (key) {
229 defineKeyPropWarningGetter(props, displayName);
230 }
231 if (ref) {
232 defineRefPropWarningGetter(props, displayName);
233 }
234 }
235 }
236 }
237 return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
238};
239
240/**
241 * Return a function that produces ReactElements of a given type.
242 * See https://facebook.github.io/react/docs/top-level-api.html#react.createfactory
243 */
244ReactElement.createFactory = function (type) {
245 var factory = ReactElement.createElement.bind(null, type);
246 // Expose the type on the factory and the prototype so that it can be
247 // easily accessed on elements. E.g. `<Foo />.type === Foo`.
248 // This should not be named `constructor` since this may not be the function
249 // that created the element, and it may not even be a constructor.
250 // Legacy hook TODO: Warn if this is accessed
251 factory.type = type;
252 return factory;
253};
254
255ReactElement.cloneAndReplaceKey = function (oldElement, newKey) {
256 var newElement = ReactElement(oldElement.type, newKey, oldElement.ref, oldElement._self, oldElement._source, oldElement._owner, oldElement.props);
257
258 return newElement;
259};
260
261/**
262 * Clone and return a new ReactElement using element as the starting point.
263 * See https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement
264 */
265ReactElement.cloneElement = function (element, config, children) {
266 var propName;
267
268 // Original props are copied
269 var props = _assign({}, element.props);
270
271 // Reserved names are extracted
272 var key = element.key;
273 var ref = element.ref;
274 // Self is preserved since the owner is preserved.
275 var self = element._self;
276 // Source is preserved since cloneElement is unlikely to be targeted by a
277 // transpiler, and the original source is probably a better indicator of the
278 // true owner.
279 var source = element._source;
280
281 // Owner will be preserved, unless ref is overridden
282 var owner = element._owner;
283
284 if (config != null) {
285 if (hasValidRef(config)) {
286 // Silently steal the ref from the parent.
287 ref = config.ref;
288 owner = ReactCurrentOwner.current;
289 }
290 if (hasValidKey(config)) {
291 key = '' + config.key;
292 }
293
294 // Remaining properties override existing props
295 var defaultProps;
296 if (element.type && element.type.defaultProps) {
297 defaultProps = element.type.defaultProps;
298 }
299 for (propName in config) {
300 if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
301 if (config[propName] === undefined && defaultProps !== undefined) {
302 // Resolve default props
303 props[propName] = defaultProps[propName];
304 } else {
305 props[propName] = config[propName];
306 }
307 }
308 }
309 }
310
311 // Children can be more than one argument, and those are transferred onto
312 // the newly allocated props object.
313 var childrenLength = arguments.length - 2;
314 if (childrenLength === 1) {
315 props.children = children;
316 } else if (childrenLength > 1) {
317 var childArray = Array(childrenLength);
318 for (var i = 0; i < childrenLength; i++) {
319 childArray[i] = arguments[i + 2];
320 }
321 props.children = childArray;
322 }
323
324 return ReactElement(element.type, key, ref, self, source, owner, props);
325};
326
327/**
328 * Verifies the object is a ReactElement.
329 * See https://facebook.github.io/react/docs/top-level-api.html#react.isvalidelement
330 * @param {?object} object
331 * @return {boolean} True if `object` is a valid component.
332 * @final
333 */
334ReactElement.isValidElement = function (object) {
335 return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
336};
337
338module.exports = ReactElement;
\No newline at end of file