Frames

ReactElement

0
1
2
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 * @providesModule ReactElement
8 */
9
10'use strict';
11
12var ReactCurrentOwner = require('ReactCurrentOwner');
13var hasOwnProperty = Object.prototype.hasOwnProperty;
14
15if (__DEV__) {
16 var warning = require('fbjs/lib/warning');
17}
18
19// The Symbol used to tag the ReactElement type. If there is no native Symbol
20// nor polyfill, then a plain number is used for performance.
21var REACT_ELEMENT_TYPE =
22 (typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
23 0xeac7;
24
25var RESERVED_PROPS = {
26 key: true,
27 ref: true,
28 __self: true,
29 __source: true,
30};
31
32var specialPropKeyWarningShown, specialPropRefWarningShown;
33
34function hasValidRef(config) {
35 if (__DEV__) {
36 if (hasOwnProperty.call(config, 'ref')) {
37 var getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
38 if (getter && getter.isReactWarning) {
39 return false;
40 }
41 }
42 }
43 return config.ref !== undefined;
44}
45
46function hasValidKey(config) {
47 if (__DEV__) {
48 if (hasOwnProperty.call(config, 'key')) {
49 var getter = Object.getOwnPropertyDescriptor(config, 'key').get;
50 if (getter && getter.isReactWarning) {
51 return false;
52 }
53 }
54 }
55 return config.key !== undefined;
56}
57
58function defineKeyPropWarningGetter(props, displayName) {
59 var warnAboutAccessingKey = function() {
60 if (!specialPropKeyWarningShown) {
61 specialPropKeyWarningShown = true;
62 warning(
63 false,
64 '%s: `key` is not a prop. Trying to access it will result ' +
65 'in `undefined` being returned. If you need to access the same ' +
66 'value within the child component, you should pass it as a different ' +
67 'prop. (https://fb.me/react-special-props)',
68 displayName,
69 );
70 }
71 };
72 warnAboutAccessingKey.isReactWarning = true;
73 Object.defineProperty(props, 'key', {
74 get: warnAboutAccessingKey,
test

75 configurable: true,
76 });
77}
78
79function defineRefPropWarningGetter(props, displayName) {
80 var warnAboutAccessingRef = function() {
81 if (!specialPropRefWarningShown) {
82 specialPropRefWarningShown = true;
83 warning(
84 false,
85 '%s: `ref` is not a prop. Trying to access it will result ' +
86 'in `undefined` being returned. If you need to access the same ' +
87 'value within the child component, you should pass it as a different ' +
88 'prop. (https://fb.me/react-special-props)',
89 displayName,
90 );
91 }
92 };
93 warnAboutAccessingRef.isReactWarning = true;
94 Object.defineProperty(props, 'ref', {
95 get: warnAboutAccessingRef,
96 configurable: true,
97 });
98}
99
100/**
101 * Factory method to create a new React element. This no longer adheres to
102 * the class pattern, so do not use new to call it. Also, no instanceof check
103 * will work. Instead test $$typeof field against Symbol.for('react.element') to check
104 * if something is a React Element.
105 *
106 * @param {*} type
107 * @param {*} key
108 * @param {string|object} ref
109 * @param {*} self A *temporary* helper to detect places where `this` is
110 * different from the `owner` when React.createElement is called, so that we
111 * can warn. We want to get rid of owner and replace string `ref`s with arrow
112 * functions, and as long as `this` and owner are the same, there will be no
113 * change in behavior.
114 * @param {*} source An annotation object (added by a transpiler or otherwise)
115 * indicating filename, line number, and/or other information.
116 * @param {*} owner
117 * @param {*} props
118 * @internal
119 */
120var ReactElement = function(type, key, ref, self, source, owner, props) {
121 var element = {
122 // This tag allow us to uniquely identify this as a React Element
123 $$typeof: REACT_ELEMENT_TYPE,
124
125 // Built-in properties that belong on the element
126 type: type,
127 key: key,
128 ref: ref,
129 props: props,
130
131 // Record the component responsible for creating this element.
132 _owner: owner,
133 };
134
135 if (__DEV__) {
136 // The validation flag is currently mutative. We put it on
137 // an external backing store so that we can freeze the whole object.
138 // This can be replaced with a WeakMap once they are implemented in
139 // commonly used development environments.
140 element._store = {};
141
142 // To make comparing ReactElements easier for testing purposes, we make
143 // the validation flag non-enumerable (where possible, which should
144 // include every environment we run tests in), so the test framework
145 // ignores it.
146 Object.defineProperty(element._store, 'validated', {
147 configurable: false,
148 enumerable: false,
149 writable: true,
150 value: false,
151 });
152 // self and source are DEV only properties.
153 Object.defineProperty(element, '_self', {
154 configurable: false,
155 enumerable: false,
156 writable: false,
157 value: self,
158 });
159 // Two elements created in two different places should be considered
160 // equal for testing purposes and therefore we hide it from enumeration.
161 Object.defineProperty(element, '_source', {
162 configurable: false,
163 enumerable: false,
164 writable: false,
165 value: source,
166 });
167 if (Object.freeze) {
168 Object.freeze(element.props);
169 Object.freeze(element);
170 }
171 }
172
173 return element;
174};
175
176/**
177 * Create and return a new ReactElement of the given type.
178 * See https://facebook.github.io/react/docs/react-api.html#createelement
179 */
180ReactElement.createElement = function(type, config, children) {
181 var propName;
182
183 // Reserved names are extracted
184 var props = {};
185
186 var key = null;
187 var ref = null;
188 var self = null;
189 var source = null;
190
191 if (config != null) {
192 if (hasValidRef(config)) {
193 ref = config.ref;
194 }
195 if (hasValidKey(config)) {
196 key = '' + config.key;
197 }
198
199 self = config.__self === undefined ? null : config.__self;
200 source = config.__source === undefined ? null : config.__source;
201 // Remaining properties are added to a new props object
202 for (propName in config) {
203 if (
204 hasOwnProperty.call(config, propName) &&
205 !RESERVED_PROPS.hasOwnProperty(propName)
206 ) {
207 props[propName] = config[propName];
208 }
209 }
210 }
211
212 // Children can be more than one argument, and those are transferred onto
213 // the newly allocated props object.
214 var childrenLength = arguments.length - 2;
215 if (childrenLength === 1) {
216 props.children = children;
217 } else if (childrenLength > 1) {
218 var childArray = Array(childrenLength);
219 for (var i = 0; i < childrenLength; i++) {
220 childArray[i] = arguments[i + 2];
221 }
222 if (__DEV__) {
223 if (Object.freeze) {
224 Object.freeze(childArray);
225 }
226 }
227 props.children = childArray;
228 }
229
230 // Resolve default props
231 if (type && type.defaultProps) {
232 var defaultProps = type.defaultProps;
233 for (propName in defaultProps) {
234 if (props[propName] === undefined) {
235 props[propName] = defaultProps[propName];
236 }
237 }
238 }
239 if (__DEV__) {
240 if (key || ref) {
241 if (
242 typeof props.$$typeof === 'undefined' ||
243 props.$$typeof !== REACT_ELEMENT_TYPE
244 ) {
245 var displayName = typeof type === 'function'
246 ? type.displayName || type.name || 'Unknown'
247 : type;
248 if (key) {
249 defineKeyPropWarningGetter(props, displayName);
250 }
251 if (ref) {
252 defineRefPropWarningGetter(props, displayName);
253 }
254 }
255 }
256 }
257 return ReactElement(
258 type,
259 key,
260 ref,
261 self,
262 source,
263 ReactCurrentOwner.current,
264 props,
265 );
266};
267
268/**
269 * Return a function that produces ReactElements of a given type.
270 * See https://facebook.github.io/react/docs/react-api.html#createfactory
271 */
272ReactElement.createFactory = function(type) {
273 var factory = ReactElement.createElement.bind(null, type);
274 // Expose the type on the factory and the prototype so that it can be
275 // easily accessed on elements. E.g. `<Foo />.type === Foo`.
276 // This should not be named `constructor` since this may not be the function
277 // that created the element, and it may not even be a constructor.
278 // Legacy hook TODO: Warn if this is accessed
279 factory.type = type;
280 return factory;
281};
282
283ReactElement.cloneAndReplaceKey = function(oldElement, newKey) {
284 var newElement = ReactElement(
285 oldElement.type,
286 newKey,
287 oldElement.ref,
288 oldElement._self,
289 oldElement._source,
290 oldElement._owner,
291 oldElement.props,
292 );
293
294 return newElement;
295};
296
297/**
298 * Clone and return a new ReactElement using element as the starting point.
299 * See https://facebook.github.io/react/docs/react-api.html#cloneelement
300 */
301ReactElement.cloneElement = function(element, config, children) {
302 var propName;
303
304 // Original props are copied
305 var props = Object.assign({}, element.props);
306
307 // Reserved names are extracted
308 var key = element.key;
309 var ref = element.ref;
310 // Self is preserved since the owner is preserved.
311 var self = element._self;
312 // Source is preserved since cloneElement is unlikely to be targeted by a
313 // transpiler, and the original source is probably a better indicator of the
314 // true owner.
315 var source = element._source;
316
317 // Owner will be preserved, unless ref is overridden
318 var owner = element._owner;
319
320 if (config != null) {
321 if (hasValidRef(config)) {
322 // Silently steal the ref from the parent.
323 ref = config.ref;
324 owner = ReactCurrentOwner.current;
325 }
326 if (hasValidKey(config)) {
327 key = '' + config.key;
328 }
329
330 // Remaining properties override existing props
331 var defaultProps;
332 if (element.type && element.type.defaultProps) {
333 defaultProps = element.type.defaultProps;
334 }
335 for (propName in config) {
336 if (
337 hasOwnProperty.call(config, propName) &&
338 !RESERVED_PROPS.hasOwnProperty(propName)
339 ) {
340 if (config[propName] === undefined && defaultProps !== undefined) {
341 // Resolve default props
342 props[propName] = defaultProps[propName];
343 } else {
344 props[propName] = config[propName];
345 }
346 }
347 }
348 }
349
350 // Children can be more than one argument, and those are transferred onto
351 // the newly allocated props object.
352 var childrenLength = arguments.length - 2;
353 if (childrenLength === 1) {
354 props.children = children;
355 } else if (childrenLength > 1) {
356 var childArray = Array(childrenLength);
357 for (var i = 0; i < childrenLength; i++) {
358 childArray[i] = arguments[i + 2];
359 }
360 props.children = childArray;
361 }
362
363 return ReactElement(element.type, key, ref, self, source, owner, props);
364};
365
366/**
367 * Verifies the object is a ReactElement.
368 * See https://facebook.github.io/react/docs/react-api.html#isvalidelement
369 * @param {?object} object
370 * @return {boolean} True if `object` is a valid component.
371 * @final
372 */
373ReactElement.isValidElement = function(object) {
374 return (
375 typeof object === 'object' &&
376 object !== null &&
377 object.$$typeof === REACT_ELEMENT_TYPE
378 );
379};
380
381module.exports = ReactElement;
382