UNPKG

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