main
js 905 lines 27.9 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
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 import getComponentNameFromType from 'shared/getComponentNameFromType';
9 import ReactSharedInternals from 'shared/ReactSharedInternals';
10 import hasOwnProperty from 'shared/hasOwnProperty';
11 import assign from 'shared/assign';
12 import {
13 REACT_ELEMENT_TYPE,
14 REACT_FRAGMENT_TYPE,
15 REACT_LAZY_TYPE,
16 REACT_OPTIMISTIC_KEY,
17 } from 'shared/ReactSymbols';
18 import {checkKeyStringCoercion} from 'shared/CheckStringCoercion';
19 import isArray from 'shared/isArray';
20 import {ownerStackLimit, enableOptimisticKey} from 'shared/ReactFeatureFlags';
21
22 const createTask =
23 // eslint-disable-next-line react-internal/no-production-logging
24 __DEV__ && console.createTask
25 ? // eslint-disable-next-line react-internal/no-production-logging
26 console.createTask
27 : () => null;
28
29 function getTaskName(type) {
30 if (type === REACT_FRAGMENT_TYPE) {
31 return '<>';
32 }
33 if (
34 typeof type === 'object' &&
35 type !== null &&
36 type.$$typeof === REACT_LAZY_TYPE
37 ) {
38 // We don't want to eagerly initialize the initializer in DEV mode so we can't
39 // call it to extract the type so we don't know the type of this component.
40 return '<...>';
41 }
42 try {
43 const name = getComponentNameFromType(type);
44 return name ? '<' + name + '>' : '<...>';
45 } catch (x) {
46 return '<...>';
47 }
48 }
49
50 function getOwner() {
51 if (__DEV__) {
52 const dispatcher = ReactSharedInternals.A;
53 if (dispatcher === null) {
54 return null;
55 }
56 return dispatcher.getOwner();
57 }
58 return null;
59 }
60
61 // v8 (Chromium, Node.js) defaults to 10
62 // SpiderMonkey (Firefox) does not support Error.stackTraceLimit
63 // JSC (Safari) defaults to 100
64 // The lower the limit, the more likely we'll not reach react_stack_bottom_frame
65 // The higher the limit, the slower Error() is when not inspecting with a debugger.
66 // When inspecting with a debugger, Error.stackTraceLimit has no impact on Error() performance (in v8).
67 const ownerStackTraceLimit = 10;
68
69 /** @noinline */
70 function UnknownOwner() {
71 /** @noinline */
72 return (() => Error('react-stack-top-frame'))();
73 }
74 const createFakeCallStack = {
75 react_stack_bottom_frame: function (callStackForError) {
76 return callStackForError();
77 },
78 };
79
80 let specialPropKeyWarningShown;
81 let didWarnAboutElementRef;
82 let didWarnAboutOldJSXRuntime;
83 let unknownOwnerDebugStack;
84 let unknownOwnerDebugTask;
85
86 if (__DEV__) {
87 didWarnAboutElementRef = {};
88
89 // We use this technique to trick minifiers to preserve the function name.
90 unknownOwnerDebugStack = createFakeCallStack.react_stack_bottom_frame.bind(
91 createFakeCallStack,
92 UnknownOwner,
93 )();
94 unknownOwnerDebugTask = createTask(getTaskName(UnknownOwner));
95 }
96
97 function hasValidRef(config) {
98 if (__DEV__) {
99 if (hasOwnProperty.call(config, 'ref')) {
100 const getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
101 if (getter && getter.isReactWarning) {
102 return false;
103 }
104 }
105 }
106 return config.ref !== undefined;
107 }
108
109 function hasValidKey(config) {
110 if (__DEV__) {
111 if (hasOwnProperty.call(config, 'key')) {
112 const getter = Object.getOwnPropertyDescriptor(config, 'key').get;
113 if (getter && getter.isReactWarning) {
114 return false;
115 }
116 }
117 }
118 return config.key !== undefined;
119 }
120
121 function defineKeyPropWarningGetter(props, displayName) {
122 if (__DEV__) {
123 const warnAboutAccessingKey = function () {
124 if (!specialPropKeyWarningShown) {
125 specialPropKeyWarningShown = true;
126 console.error(
127 '%s: `key` is not a prop. Trying to access it will result ' +
128 'in `undefined` being returned. If you need to access the same ' +
129 'value within the child component, you should pass it as a different ' +
130 'prop. (https://react.dev/link/special-props)',
131 displayName,
132 );
133 }
134 };
135 warnAboutAccessingKey.isReactWarning = true;
136 Object.defineProperty(props, 'key', {
137 get: warnAboutAccessingKey,
138 configurable: true,
139 });
140 }
141 }
142
143 function elementRefGetterWithDeprecationWarning() {
144 if (__DEV__) {
145 const componentName = getComponentNameFromType(this.type);
146 if (!didWarnAboutElementRef[componentName]) {
147 didWarnAboutElementRef[componentName] = true;
148 console.error(
149 'Accessing element.ref was removed in React 19. ref is now a ' +
150 'regular prop. It will be removed from the JSX Element ' +
151 'type in a future release.',
152 );
153 }
154
155 // An undefined `element.ref` is coerced to `null` for
156 // backwards compatibility.
157 const refProp = this.props.ref;
158 return refProp !== undefined ? refProp : null;
159 }
160 }
161
162 /**
163 * Factory method to create a new React element. This no longer adheres to
164 * the class pattern, so do not use new to call it. Also, instanceof check
165 * will not work. Instead test $$typeof field against Symbol.for('react.transitional.element') to check
166 * if something is a React Element.
167 *
168 * @internal
169 */
170 function ReactElement(type, key, props, owner, debugStack, debugTask) {
171 // Ignore whatever was passed as the ref argument and treat `props.ref` as
172 // the source of truth. The only thing we use this for is `element.ref`,
173 // which will log a deprecation warning on access. In the next release, we
174 // can remove `element.ref` as well as the `ref` argument.
175 const refProp = props.ref;
176
177 // An undefined `element.ref` is coerced to `null` for
178 // backwards compatibility.
179 const ref = refProp !== undefined ? refProp : null;
180
181 let element;
182 if (__DEV__) {
183 // In dev, make `ref` a non-enumerable property with a warning. It's non-
184 // enumerable so that test matchers and serializers don't access it and
185 // trigger the warning.
186 //
187 // `ref` will be removed from the element completely in a future release.
188 element = {
189 // This tag allows us to uniquely identify this as a React Element
190 $$typeof: REACT_ELEMENT_TYPE,
191
192 // Built-in properties that belong on the element
193 type,
194 key,
195
196 props,
197
198 // Record the component responsible for creating this element.
199 _owner: owner,
200 };
201 if (ref !== null) {
202 Object.defineProperty(element, 'ref', {
203 enumerable: false,
204 get: elementRefGetterWithDeprecationWarning,
205 });
206 } else {
207 // Don't warn on access if a ref is not given. This reduces false
208 // positives in cases where a test serializer uses
209 // getOwnPropertyDescriptors to compare objects, like Jest does, which is
210 // a problem because it bypasses non-enumerability.
211 //
212 // So unfortunately this will trigger a false positive warning in Jest
213 // when the diff is printed:
214 //
215 // expect(<div ref={ref} />).toEqual(<span ref={ref} />);
216 //
217 // A bit sketchy, but this is what we've done for the `props.key` and
218 // `props.ref` accessors for years, which implies it will be good enough
219 // for `element.ref`, too. Let's see if anyone complains.
220 Object.defineProperty(element, 'ref', {
221 enumerable: false,
222 value: null,
223 });
224 }
225 } else {
226 // In prod, `ref` is a regular property and _owner doesn't exist.
227 element = {
228 // This tag allows us to uniquely identify this as a React Element
229 $$typeof: REACT_ELEMENT_TYPE,
230
231 // Built-in properties that belong on the element
232 type,
233 key,
234 ref,
235
236 props,
237 };
238 }
239
240 if (__DEV__) {
241 // The validation flag is currently mutative. We put it on
242 // an external backing store so that we can freeze the whole object.
243 // This can be replaced with a WeakMap once they are implemented in
244 // commonly used development environments.
245 element._store = {};
246
247 // To make comparing ReactElements easier for testing purposes, we make
248 // the validation flag non-enumerable (where possible, which should
249 // include every environment we run tests in), so the test framework
250 // ignores it.
251 Object.defineProperty(element._store, 'validated', {
252 configurable: false,
253 enumerable: false,
254 writable: true,
255 value: 0,
256 });
257 // debugInfo contains Server Component debug information.
258 Object.defineProperty(element, '_debugInfo', {
259 configurable: false,
260 enumerable: false,
261 writable: true,
262 value: null,
263 });
264 Object.defineProperty(element, '_debugStack', {
265 configurable: false,
266 enumerable: false,
267 writable: true,
268 value: debugStack,
269 });
270 Object.defineProperty(element, '_debugTask', {
271 configurable: false,
272 enumerable: false,
273 writable: true,
274 value: debugTask,
275 });
276 if (Object.freeze) {
277 Object.freeze(element.props);
278 Object.freeze(element);
279 }
280 }
281
282 return element;
283 }
284
285 /**
286 * https://github.com/reactjs/rfcs/pull/107
287 * @param {*} type
288 * @param {object} props
289 * @param {string} key
290 */
291 export function jsxProd(type, config, maybeKey) {
292 let key = null;
293
294 // Currently, key can be spread in as a prop. This causes a potential
295 // issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
296 // or <div key="Hi" {...props} /> ). We want to deprecate key spread,
297 // but as an intermediary step, we will use jsxDEV for everything except
298 // <div {...props} key="Hi" />, because we aren't currently able to tell if
299 // key is explicitly declared to be undefined or not.
300 if (maybeKey !== undefined) {
301 if (enableOptimisticKey && maybeKey === REACT_OPTIMISTIC_KEY) {
302 key = REACT_OPTIMISTIC_KEY;
303 } else {
304 if (__DEV__) {
305 checkKeyStringCoercion(maybeKey);
306 }
307 key = '' + maybeKey;
308 }
309 }
310
311 if (hasValidKey(config)) {
312 if (enableOptimisticKey && maybeKey === REACT_OPTIMISTIC_KEY) {
313 key = REACT_OPTIMISTIC_KEY;
314 } else {
315 if (__DEV__) {
316 checkKeyStringCoercion(config.key);
317 }
318 key = '' + config.key;
319 }
320 }
321
322 let props;
323 if (!('key' in config)) {
324 // If key was not spread in, we can reuse the original props object. This
325 // only works for `jsx`, not `createElement`, because `jsx` is a compiler
326 // target and the compiler always passes a new object. For `createElement`,
327 // we can't assume a new object is passed every time because it can be
328 // called manually.
329 //
330 // Spreading key is a warning in dev. In a future release, we will not
331 // remove a spread key from the props object. (But we'll still warn.) We'll
332 // always pass the object straight through.
333 props = config;
334 } else {
335 // We need to remove reserved props (key, prop, ref). Create a fresh props
336 // object and copy over all the non-reserved props. We don't use `delete`
337 // because in V8 it will deopt the object to dictionary mode.
338 props = {};
339 for (const propName in config) {
340 // Skip over reserved prop names
341 if (propName !== 'key') {
342 props[propName] = config[propName];
343 }
344 }
345 }
346
347 return ReactElement(type, key, props, getOwner(), undefined, undefined);
348 }
349
350 // While `jsxDEV` should never be called when running in production, we do
351 // support `jsx` and `jsxs` when running in development. This supports the case
352 // where a third-party dependency ships code that was compiled for production;
353 // we want to still provide warnings in development.
354 //
355 // So these functions are the _dev_ implementations of the _production_
356 // API signatures.
357 //
358 // Since these functions are dev-only, it's ok to add an indirection here. They
359 // only exist to provide different versions of `isStaticChildren`. (We shouldn't
360 // use this pattern for the prod versions, though, because it will add an call
361 // frame.)
362 export function jsxProdSignatureRunningInDevWithDynamicChildren(
363 type,
364 config,
365 maybeKey,
366 ) {
367 if (__DEV__) {
368 const isStaticChildren = false;
369 const trackActualOwner =
370 __DEV__ &&
371 ReactSharedInternals.recentlyCreatedOwnerStacks++ < ownerStackLimit;
372 let debugStackDEV = false;
373 if (__DEV__) {
374 if (trackActualOwner) {
375 const previousStackTraceLimit = Error.stackTraceLimit;
376 Error.stackTraceLimit = ownerStackTraceLimit;
377 debugStackDEV = Error('react-stack-top-frame');
378 Error.stackTraceLimit = previousStackTraceLimit;
379 } else {
380 debugStackDEV = unknownOwnerDebugStack;
381 }
382 }
383
384 return jsxDEVImpl(
385 type,
386 config,
387 maybeKey,
388 isStaticChildren,
389 debugStackDEV,
390 __DEV__ &&
391 (trackActualOwner
392 ? createTask(getTaskName(type))
393 : unknownOwnerDebugTask),
394 );
395 }
396 }
397
398 export function jsxProdSignatureRunningInDevWithStaticChildren(
399 type,
400 config,
401 maybeKey,
402 ) {
403 if (__DEV__) {
404 const isStaticChildren = true;
405 const trackActualOwner =
406 __DEV__ &&
407 ReactSharedInternals.recentlyCreatedOwnerStacks++ < ownerStackLimit;
408 let debugStackDEV = false;
409 if (__DEV__) {
410 if (trackActualOwner) {
411 const previousStackTraceLimit = Error.stackTraceLimit;
412 Error.stackTraceLimit = ownerStackTraceLimit;
413 debugStackDEV = Error('react-stack-top-frame');
414 Error.stackTraceLimit = previousStackTraceLimit;
415 } else {
416 debugStackDEV = unknownOwnerDebugStack;
417 }
418 }
419 return jsxDEVImpl(
420 type,
421 config,
422 maybeKey,
423 isStaticChildren,
424 debugStackDEV,
425 __DEV__ &&
426 (trackActualOwner
427 ? createTask(getTaskName(type))
428 : unknownOwnerDebugTask),
429 );
430 }
431 }
432
433 const didWarnAboutKeySpread = {};
434
435 /**
436 * https://github.com/reactjs/rfcs/pull/107
437 * @param {*} type
438 * @param {object} props
439 * @param {string} key
440 */
441 export function jsxDEV(type, config, maybeKey, isStaticChildren) {
442 const trackActualOwner =
443 __DEV__ &&
444 ReactSharedInternals.recentlyCreatedOwnerStacks++ < ownerStackLimit;
445 let debugStackDEV = false;
446 if (__DEV__) {
447 if (trackActualOwner) {
448 const previousStackTraceLimit = Error.stackTraceLimit;
449 Error.stackTraceLimit = ownerStackTraceLimit;
450 debugStackDEV = Error('react-stack-top-frame');
451 Error.stackTraceLimit = previousStackTraceLimit;
452 } else {
453 debugStackDEV = unknownOwnerDebugStack;
454 }
455 }
456 return jsxDEVImpl(
457 type,
458 config,
459 maybeKey,
460 isStaticChildren,
461 debugStackDEV,
462 __DEV__ &&
463 (trackActualOwner
464 ? createTask(getTaskName(type))
465 : unknownOwnerDebugTask),
466 );
467 }
468
469 function jsxDEVImpl(
470 type,
471 config,
472 maybeKey,
473 isStaticChildren,
474 debugStack,
475 debugTask,
476 ) {
477 if (__DEV__) {
478 // We don't warn for invalid element type here because with owner stacks,
479 // we error in the renderer. The renderer is the only one that knows what
480 // types are valid for this particular renderer so we let it error there.
481
482 // Skip key warning if the type isn't valid since our key validation logic
483 // doesn't expect a non-string/function type and can throw confusing
484 // errors. We don't want exception behavior to differ between dev and
485 // prod. (Rendering will throw with a helpful message and as soon as the
486 // type is fixed, the key warnings will appear.)
487 // With owner stacks, we no longer need the type here so this comment is
488 // no longer true. Which is why we can run this even for invalid types.
489 const children = config.children;
490 if (children !== undefined) {
491 if (isStaticChildren) {
492 if (isArray(children)) {
493 for (let i = 0; i < children.length; i++) {
494 validateChildKeys(children[i]);
495 }
496
497 if (Object.freeze) {
498 Object.freeze(children);
499 }
500 } else {
501 console.error(
502 'React.jsx: Static children should always be an array. ' +
503 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' +
504 'Use the Babel transform instead.',
505 );
506 }
507 } else {
508 validateChildKeys(children);
509 }
510 }
511
512 // Warn about key spread regardless of whether the type is valid.
513 if (hasOwnProperty.call(config, 'key')) {
514 const componentName = getComponentNameFromType(type);
515 const keys = Object.keys(config).filter(k => k !== 'key');
516 const beforeExample =
517 keys.length > 0
518 ? '{key: someKey, ' + keys.join(': ..., ') + ': ...}'
519 : '{key: someKey}';
520 if (!didWarnAboutKeySpread[componentName + beforeExample]) {
521 const afterExample =
522 keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}';
523 console.error(
524 'A props object containing a "key" prop is being spread into JSX:\n' +
525 ' let props = %s;\n' +
526 ' <%s {...props} />\n' +
527 'React keys must be passed directly to JSX without using spread:\n' +
528 ' let props = %s;\n' +
529 ' <%s key={someKey} {...props} />',
530 beforeExample,
531 componentName,
532 afterExample,
533 componentName,
534 );
535 didWarnAboutKeySpread[componentName + beforeExample] = true;
536 }
537 }
538
539 let key = null;
540
541 // Currently, key can be spread in as a prop. This causes a potential
542 // issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
543 // or <div key="Hi" {...props} /> ). We want to deprecate key spread,
544 // but as an intermediary step, we will use jsxDEV for everything except
545 // <div {...props} key="Hi" />, because we aren't currently able to tell if
546 // key is explicitly declared to be undefined or not.
547 if (maybeKey !== undefined) {
548 if (enableOptimisticKey && maybeKey === REACT_OPTIMISTIC_KEY) {
549 key = REACT_OPTIMISTIC_KEY;
550 } else {
551 if (__DEV__) {
552 checkKeyStringCoercion(maybeKey);
553 }
554 key = '' + maybeKey;
555 }
556 }
557
558 if (hasValidKey(config)) {
559 if (enableOptimisticKey && config.key === REACT_OPTIMISTIC_KEY) {
560 key = REACT_OPTIMISTIC_KEY;
561 } else {
562 if (__DEV__) {
563 checkKeyStringCoercion(config.key);
564 }
565 key = '' + config.key;
566 }
567 }
568
569 let props;
570 if (!('key' in config)) {
571 // If key was not spread in, we can reuse the original props object. This
572 // only works for `jsx`, not `createElement`, because `jsx` is a compiler
573 // target and the compiler always passes a new object. For `createElement`,
574 // we can't assume a new object is passed every time because it can be
575 // called manually.
576 //
577 // Spreading key is a warning in dev. In a future release, we will not
578 // remove a spread key from the props object. (But we'll still warn.) We'll
579 // always pass the object straight through.
580 props = config;
581 } else {
582 // We need to remove reserved props (key, prop, ref). Create a fresh props
583 // object and copy over all the non-reserved props. We don't use `delete`
584 // because in V8 it will deopt the object to dictionary mode.
585 props = {};
586 for (const propName in config) {
587 // Skip over reserved prop names
588 if (propName !== 'key') {
589 props[propName] = config[propName];
590 }
591 }
592 }
593
594 if (key) {
595 const displayName =
596 typeof type === 'function'
597 ? type.displayName || type.name || 'Unknown'
598 : type;
599 defineKeyPropWarningGetter(props, displayName);
600 }
601
602 return ReactElement(type, key, props, getOwner(), debugStack, debugTask);
603 }
604 }
605
606 /**
607 * Create and return a new ReactElement of the given type.
608 * See https://reactjs.org/docs/react-api.html#createelement
609 */
610 export function createElement(type, config, children) {
611 if (__DEV__) {
612 // We don't warn for invalid element type here because with owner stacks,
613 // we error in the renderer. The renderer is the only one that knows what
614 // types are valid for this particular renderer so we let it error there.
615
616 // Skip key warning if the type isn't valid since our key validation logic
617 // doesn't expect a non-string/function type and can throw confusing
618 // errors. We don't want exception behavior to differ between dev and
619 // prod. (Rendering will throw with a helpful message and as soon as the
620 // type is fixed, the key warnings will appear.)
621 for (let i = 2; i < arguments.length; i++) {
622 validateChildKeys(arguments[i]);
623 }
624
625 // Unlike the jsx() runtime, createElement() doesn't warn about key spread.
626 }
627
628 let propName;
629
630 // Reserved names are extracted
631 const props = {};
632
633 let key = null;
634
635 if (config != null) {
636 if (__DEV__) {
637 if (
638 !didWarnAboutOldJSXRuntime &&
639 '__self' in config &&
640 // Do not assume this is the result of an oudated JSX transform if key
641 // is present, because the modern JSX transform sometimes outputs
642 // createElement to preserve precedence between a static key and a
643 // spread key. To avoid false positive warnings, we never warn if
644 // there's a key.
645 !('key' in config)
646 ) {
647 didWarnAboutOldJSXRuntime = true;
648 console.warn(
649 'Your app (or one of its dependencies) is using an outdated JSX ' +
650 'transform. Update to the modern JSX transform for ' +
651 'faster performance: https://react.dev/link/new-jsx-transform',
652 );
653 }
654 }
655
656 if (hasValidKey(config)) {
657 if (enableOptimisticKey && config.key === REACT_OPTIMISTIC_KEY) {
658 key = REACT_OPTIMISTIC_KEY;
659 } else {
660 if (__DEV__) {
661 checkKeyStringCoercion(config.key);
662 }
663 key = '' + config.key;
664 }
665 }
666
667 // Remaining properties are added to a new props object
668 for (propName in config) {
669 if (
670 hasOwnProperty.call(config, propName) &&
671 // Skip over reserved prop names
672 propName !== 'key' &&
673 // Even though we don't use these anymore in the runtime, we don't want
674 // them to appear as props, so in createElement we filter them out.
675 // We don't have to do this in the jsx() runtime because the jsx()
676 // transform never passed these as props; it used separate arguments.
677 propName !== '__self' &&
678 propName !== '__source'
679 ) {
680 props[propName] = config[propName];
681 }
682 }
683 }
684
685 // Children can be more than one argument, and those are transferred onto
686 // the newly allocated props object.
687 const childrenLength = arguments.length - 2;
688 if (childrenLength === 1) {
689 props.children = children;
690 } else if (childrenLength > 1) {
691 const childArray = Array(childrenLength);
692 for (let i = 0; i < childrenLength; i++) {
693 childArray[i] = arguments[i + 2];
694 }
695 if (__DEV__) {
696 if (Object.freeze) {
697 Object.freeze(childArray);
698 }
699 }
700 props.children = childArray;
701 }
702
703 // Resolve default props
704 if (type && type.defaultProps) {
705 const defaultProps = type.defaultProps;
706 for (propName in defaultProps) {
707 if (props[propName] === undefined) {
708 props[propName] = defaultProps[propName];
709 }
710 }
711 }
712 if (__DEV__) {
713 if (key) {
714 const displayName =
715 typeof type === 'function'
716 ? type.displayName || type.name || 'Unknown'
717 : type;
718 defineKeyPropWarningGetter(props, displayName);
719 }
720 }
721 const trackActualOwner =
722 __DEV__ &&
723 ReactSharedInternals.recentlyCreatedOwnerStacks++ < ownerStackLimit;
724 let debugStackDEV = false;
725 if (__DEV__) {
726 if (trackActualOwner) {
727 const previousStackTraceLimit = Error.stackTraceLimit;
728 Error.stackTraceLimit = ownerStackTraceLimit;
729 debugStackDEV = Error('react-stack-top-frame');
730 Error.stackTraceLimit = previousStackTraceLimit;
731 } else {
732 debugStackDEV = unknownOwnerDebugStack;
733 }
734 }
735 return ReactElement(
736 type,
737 key,
738 props,
739 getOwner(),
740 debugStackDEV,
741 __DEV__ &&
742 (trackActualOwner
743 ? createTask(getTaskName(type))
744 : unknownOwnerDebugTask),
745 );
746 }
747
748 export function cloneAndReplaceKey(oldElement, newKey) {
749 const clonedElement = ReactElement(
750 oldElement.type,
751 newKey,
752 oldElement.props,
753 !__DEV__ ? undefined : oldElement._owner,
754 __DEV__ && oldElement._debugStack,
755 __DEV__ && oldElement._debugTask,
756 );
757 if (__DEV__) {
758 // The cloned element should inherit the original element's key validation.
759 if (oldElement._store) {
760 clonedElement._store.validated = oldElement._store.validated;
761 }
762 }
763 return clonedElement;
764 }
765
766 /**
767 * Clone and return a new ReactElement using element as the starting point.
768 * See https://reactjs.org/docs/react-api.html#cloneelement
769 */
770 export function cloneElement(element, config, children) {
771 if (element === null || element === undefined) {
772 throw new Error(
773 `The argument must be a React element, but you passed ${element}.`,
774 );
775 }
776
777 let propName;
778
779 // Original props are copied
780 const props = assign({}, element.props);
781
782 // Reserved names are extracted
783 let key = element.key;
784
785 // Owner will be preserved, unless ref is overridden
786 let owner = !__DEV__ ? undefined : element._owner;
787
788 if (config != null) {
789 if (hasValidRef(config)) {
790 owner = __DEV__ ? getOwner() : undefined;
791 }
792 if (hasValidKey(config)) {
793 if (enableOptimisticKey && config.key === REACT_OPTIMISTIC_KEY) {
794 key = REACT_OPTIMISTIC_KEY;
795 } else {
796 if (__DEV__) {
797 checkKeyStringCoercion(config.key);
798 }
799 key = '' + config.key;
800 }
801 }
802
803 // Remaining properties override existing props
804 for (propName in config) {
805 if (
806 hasOwnProperty.call(config, propName) &&
807 // Skip over reserved prop names
808 propName !== 'key' &&
809 // ...and maybe these, too, though we currently rely on them for
810 // warnings and debug information in dev. Need to decide if we're OK
811 // with dropping them. In the jsx() runtime it's not an issue because
812 // the data gets passed as separate arguments instead of props, but
813 // it would be nice to stop relying on them entirely so we can drop
814 // them from the internal Fiber field.
815 propName !== '__self' &&
816 propName !== '__source' &&
817 // Undefined `ref` is ignored by cloneElement. We treat it the same as
818 // if the property were missing. This is mostly for
819 // backwards compatibility.
820 !(propName === 'ref' && config.ref === undefined)
821 ) {
822 props[propName] = config[propName];
823 }
824 }
825 }
826
827 // Children can be more than one argument, and those are transferred onto
828 // the newly allocated props object.
829 const childrenLength = arguments.length - 2;
830 if (childrenLength === 1) {
831 props.children = children;
832 } else if (childrenLength > 1) {
833 const childArray = Array(childrenLength);
834 for (let i = 0; i < childrenLength; i++) {
835 childArray[i] = arguments[i + 2];
836 }
837 props.children = childArray;
838 }
839
840 const clonedElement = ReactElement(
841 element.type,
842 key,
843 props,
844 owner,
845 __DEV__ && element._debugStack,
846 __DEV__ && element._debugTask,
847 );
848
849 for (let i = 2; i < arguments.length; i++) {
850 validateChildKeys(arguments[i]);
851 }
852
853 return clonedElement;
854 }
855
856 /**
857 * Ensure that every element either is passed in a static location, in an
858 * array with an explicit keys property defined, or in an object literal
859 * with valid key property.
860 *
861 * @internal
862 * @param {ReactNode} node Statically passed child of any type.
863 */
864 function validateChildKeys(node) {
865 if (__DEV__) {
866 // Mark elements as being in a valid static child position so they
867 // don't need keys.
868 if (isValidElement(node)) {
869 if (node._store) {
870 node._store.validated = 1;
871 }
872 } else if (isLazyType(node)) {
873 if (node._payload.status === 'fulfilled') {
874 if (isValidElement(node._payload.value) && node._payload.value._store) {
875 node._payload.value._store.validated = 1;
876 }
877 } else if (node._store) {
878 node._store.validated = 1;
879 }
880 }
881 }
882 }
883
884 /**
885 * Verifies the object is a ReactElement.
886 * See https://reactjs.org/docs/react-api.html#isvalidelement
887 * @param {?object} object
888 * @return {boolean} True if `object` is a ReactElement.
889 * @final
890 */
891 export function isValidElement(object) {
892 return (
893 typeof object === 'object' &&
894 object !== null &&
895 object.$$typeof === REACT_ELEMENT_TYPE
896 );
897 }
898
899 export function isLazyType(object) {
900 return (
901 typeof object === 'object' &&
902 object !== null &&
903 object.$$typeof === REACT_LAZY_TYPE
904 );
905 }