1016 lines
40 KiB
JavaScript
1016 lines
40 KiB
JavaScript
|
/**
|
||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*/
|
||
|
|
||
|
import getComponentNameFromType from 'shared/getComponentNameFromType';
|
||
|
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||
|
import hasOwnProperty from 'shared/hasOwnProperty';
|
||
|
import assign from 'shared/assign';
|
||
|
import { getIteratorFn, REACT_ELEMENT_TYPE, REACT_FRAGMENT_TYPE, REACT_LAZY_TYPE } from 'shared/ReactSymbols';
|
||
|
import { checkKeyStringCoercion } from 'shared/CheckStringCoercion';
|
||
|
import isValidElementType from 'shared/isValidElementType';
|
||
|
import isArray from 'shared/isArray';
|
||
|
import { describeUnknownElementTypeFrameInDEV } from 'shared/ReactComponentStackFrame';
|
||
|
import { enableRefAsProp, disableStringRefs, disableDefaultPropsExceptForClasses, enableFastJSX, enableOwnerStacks } from 'shared/ReactFeatureFlags';
|
||
|
import { checkPropStringCoercion } from 'shared/CheckStringCoercion';
|
||
|
import { ClassComponent } from 'react-reconciler/src/ReactWorkTags';
|
||
|
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
|
||
|
const REACT_CLIENT_REFERENCE = 'react.client.reference' |> Symbol.for(%);
|
||
|
const createTask =
|
||
|
// eslint-disable-next-line react-internal/no-production-logging
|
||
|
__DEV__ && enableOwnerStacks && console.createTask ?
|
||
|
// eslint-disable-next-line react-internal/no-production-logging
|
||
|
console.createTask : () => null;
|
||
|
function getTaskName(type) {
|
||
|
if (type === REACT_FRAGMENT_TYPE) {
|
||
|
return '<>';
|
||
|
}
|
||
|
if (typeof type === 'object' && type !== null && type.$$typeof === REACT_LAZY_TYPE) {
|
||
|
// We don't want to eagerly initialize the initializer in DEV mode so we can't
|
||
|
// call it to extract the type so we don't know the type of this component.
|
||
|
return '<...>';
|
||
|
}
|
||
|
try {
|
||
|
const name = type |> getComponentNameFromType(%);
|
||
|
return name ? '<' + name + '>' : '<...>';
|
||
|
} catch (x) {
|
||
|
return '<...>';
|
||
|
}
|
||
|
}
|
||
|
function getOwner() {
|
||
|
if (__DEV__ || !disableStringRefs) {
|
||
|
const dispatcher = ReactSharedInternals.A;
|
||
|
if (dispatcher === null) {
|
||
|
return null;
|
||
|
}
|
||
|
return dispatcher.getOwner();
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
let specialPropKeyWarningShown;
|
||
|
let specialPropRefWarningShown;
|
||
|
let didWarnAboutStringRefs;
|
||
|
let didWarnAboutElementRef;
|
||
|
let didWarnAboutOldJSXRuntime;
|
||
|
if (__DEV__) {
|
||
|
didWarnAboutStringRefs = {};
|
||
|
didWarnAboutElementRef = {};
|
||
|
}
|
||
|
const enableFastJSXWithStringRefs = enableFastJSX && enableRefAsProp;
|
||
|
const enableFastJSXWithoutStringRefs = enableFastJSXWithStringRefs && disableStringRefs;
|
||
|
function hasValidRef(config) {
|
||
|
if (__DEV__) {
|
||
|
if (config |> hasOwnProperty.call(%, 'ref')) {
|
||
|
const getter = (config |> Object.getOwnPropertyDescriptor(%, 'ref')).get;
|
||
|
if (getter && getter.isReactWarning) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return config.ref !== undefined;
|
||
|
}
|
||
|
function hasValidKey(config) {
|
||
|
if (__DEV__) {
|
||
|
if (config |> hasOwnProperty.call(%, 'key')) {
|
||
|
const getter = (config |> Object.getOwnPropertyDescriptor(%, 'key')).get;
|
||
|
if (getter && getter.isReactWarning) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return config.key !== undefined;
|
||
|
}
|
||
|
function warnIfStringRefCannotBeAutoConverted(config, self) {
|
||
|
if (__DEV__) {
|
||
|
let owner;
|
||
|
if (!disableStringRefs && typeof config.ref === 'string' && (owner = getOwner()) && self && owner.stateNode !== self) {
|
||
|
const componentName = owner.type |> getComponentNameFromType(%);
|
||
|
if (!didWarnAboutStringRefs[componentName]) {
|
||
|
console.error('Component "%s" contains the string ref "%s". ' + 'Support for string refs will be removed in a future major release. ' + 'This case cannot be automatically converted to an arrow function. ' + 'We ask you to manually fix this case by using useRef() or createRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://react.dev/link/strict-mode-string-ref', owner.type |> getComponentNameFromType(%), config.ref);
|
||
|
didWarnAboutStringRefs[componentName] = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function defineKeyPropWarningGetter(props, displayName) {
|
||
|
if (__DEV__) {
|
||
|
const warnAboutAccessingKey = function () {
|
||
|
if (!specialPropKeyWarningShown) {
|
||
|
specialPropKeyWarningShown = true;
|
||
|
'%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://react.dev/link/special-props)' |> console.error(%, displayName);
|
||
|
}
|
||
|
};
|
||
|
warnAboutAccessingKey.isReactWarning = true;
|
||
|
Object.defineProperty(props, 'key', {
|
||
|
get: warnAboutAccessingKey,
|
||
|
configurable: true
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
function defineRefPropWarningGetter(props, displayName) {
|
||
|
if (!enableRefAsProp) {
|
||
|
if (__DEV__) {
|
||
|
const warnAboutAccessingRef = function () {
|
||
|
if (!specialPropRefWarningShown) {
|
||
|
specialPropRefWarningShown = true;
|
||
|
'%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://react.dev/link/special-props)' |> console.error(%, displayName);
|
||
|
}
|
||
|
};
|
||
|
warnAboutAccessingRef.isReactWarning = true;
|
||
|
Object.defineProperty(props, 'ref', {
|
||
|
get: warnAboutAccessingRef,
|
||
|
configurable: true
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function elementRefGetterWithDeprecationWarning() {
|
||
|
if (__DEV__) {
|
||
|
const componentName = this.type |> getComponentNameFromType(%);
|
||
|
if (!didWarnAboutElementRef[componentName]) {
|
||
|
didWarnAboutElementRef[componentName] = true;
|
||
|
'Accessing element.ref was removed in React 19. ref is now a ' + 'regular prop. It will be removed from the JSX Element ' + 'type in a future release.' |> console.error(%);
|
||
|
}
|
||
|
|
||
|
// An undefined `element.ref` is coerced to `null` for
|
||
|
// backwards compatibility.
|
||
|
const refProp = this.props.ref;
|
||
|
return refProp !== undefined ? refProp : null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* 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, instanceof check
|
||
|
* will not work. Instead test $$typeof field against Symbol.for('react.transitional.element') to check
|
||
|
* if something is a React Element.
|
||
|
*
|
||
|
* @param {*} type
|
||
|
* @param {*} props
|
||
|
* @param {*} key
|
||
|
* @param {string|object} ref
|
||
|
* @param {*} owner
|
||
|
* @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.
|
||
|
* @internal
|
||
|
*/
|
||
|
function ReactElement(type, key, _ref, self, source, owner, props, debugStack, debugTask) {
|
||
|
let ref;
|
||
|
if (enableRefAsProp) {
|
||
|
// When enableRefAsProp is on, ignore whatever was passed as the ref
|
||
|
// argument and treat `props.ref` as the source of truth. The only thing we
|
||
|
// use this for is `element.ref`, which will log a deprecation warning on
|
||
|
// access. In the next release, we can remove `element.ref` as well as the
|
||
|
// `ref` argument.
|
||
|
const refProp = props.ref;
|
||
|
|
||
|
// An undefined `element.ref` is coerced to `null` for
|
||
|
// backwards compatibility.
|
||
|
ref = refProp !== undefined ? refProp : null;
|
||
|
} else {
|
||
|
ref = _ref;
|
||
|
}
|
||
|
let element;
|
||
|
if (__DEV__ && enableRefAsProp) {
|
||
|
// In dev, make `ref` a non-enumerable property with a warning. It's non-
|
||
|
// enumerable so that test matchers and serializers don't access it and
|
||
|
// trigger the warning.
|
||
|
//
|
||
|
// `ref` will be removed from the element completely in a future release.
|
||
|
element = {
|
||
|
// This tag allows us to uniquely identify this as a React Element
|
||
|
$$typeof: REACT_ELEMENT_TYPE,
|
||
|
// Built-in properties that belong on the element
|
||
|
type,
|
||
|
key,
|
||
|
props,
|
||
|
// Record the component responsible for creating this element.
|
||
|
_owner: owner
|
||
|
};
|
||
|
if (ref !== null) {
|
||
|
Object.defineProperty(element, 'ref', {
|
||
|
enumerable: false,
|
||
|
get: elementRefGetterWithDeprecationWarning
|
||
|
});
|
||
|
} else {
|
||
|
// Don't warn on access if a ref is not given. This reduces false
|
||
|
// positives in cases where a test serializer uses
|
||
|
// getOwnPropertyDescriptors to compare objects, like Jest does, which is
|
||
|
// a problem because it bypasses non-enumerability.
|
||
|
//
|
||
|
// So unfortunately this will trigger a false positive warning in Jest
|
||
|
// when the diff is printed:
|
||
|
//
|
||
|
// expect(<div ref={ref} />).toEqual(<span ref={ref} />);
|
||
|
//
|
||
|
// A bit sketchy, but this is what we've done for the `props.key` and
|
||
|
// `props.ref` accessors for years, which implies it will be good enough
|
||
|
// for `element.ref`, too. Let's see if anyone complains.
|
||
|
Object.defineProperty(element, 'ref', {
|
||
|
enumerable: false,
|
||
|
value: null
|
||
|
});
|
||
|
}
|
||
|
} else if (!__DEV__ && disableStringRefs) {
|
||
|
// In prod, `ref` is a regular property and _owner doesn't exist.
|
||
|
element = {
|
||
|
// This tag allows us to uniquely identify this as a React Element
|
||
|
$$typeof: REACT_ELEMENT_TYPE,
|
||
|
// Built-in properties that belong on the element
|
||
|
type,
|
||
|
key,
|
||
|
ref,
|
||
|
props
|
||
|
};
|
||
|
} else {
|
||
|
// In prod, `ref` is a regular property. It will be removed in a
|
||
|
// future release.
|
||
|
element = {
|
||
|
// This tag allows us to uniquely identify this as a React Element
|
||
|
$$typeof: REACT_ELEMENT_TYPE,
|
||
|
// Built-in properties that belong on the element
|
||
|
type,
|
||
|
key,
|
||
|
ref,
|
||
|
props,
|
||
|
// Record the component responsible for creating this element.
|
||
|
_owner: owner
|
||
|
};
|
||
|
}
|
||
|
if (__DEV__) {
|
||
|
// 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.
|
||
|
Object.defineProperty(element._store, 'validated', {
|
||
|
configurable: false,
|
||
|
enumerable: false,
|
||
|
writable: true,
|
||
|
value: false
|
||
|
});
|
||
|
// debugInfo contains Server Component debug information.
|
||
|
Object.defineProperty(element, '_debugInfo', {
|
||
|
configurable: false,
|
||
|
enumerable: false,
|
||
|
writable: true,
|
||
|
value: null
|
||
|
});
|
||
|
if (enableOwnerStacks) {
|
||
|
Object.defineProperty(element, '_debugStack', {
|
||
|
configurable: false,
|
||
|
enumerable: false,
|
||
|
writable: true,
|
||
|
value: debugStack
|
||
|
});
|
||
|
Object.defineProperty(element, '_debugTask', {
|
||
|
configurable: false,
|
||
|
enumerable: false,
|
||
|
writable: true,
|
||
|
value: debugTask
|
||
|
});
|
||
|
}
|
||
|
if (Object.freeze) {
|
||
|
element.props |> Object.freeze(%);
|
||
|
element |> Object.freeze(%);
|
||
|
}
|
||
|
}
|
||
|
return element;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* https://github.com/reactjs/rfcs/pull/107
|
||
|
* @param {*} type
|
||
|
* @param {object} props
|
||
|
* @param {string} key
|
||
|
*/
|
||
|
export function jsxProd(type, config, maybeKey) {
|
||
|
let key = null;
|
||
|
let ref = null;
|
||
|
|
||
|
// Currently, key can be spread in as a prop. This causes a potential
|
||
|
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
|
||
|
// or <div key="Hi" {...props} /> ). We want to deprecate key spread,
|
||
|
// but as an intermediary step, we will use jsxDEV for everything except
|
||
|
// <div {...props} key="Hi" />, because we aren't currently able to tell if
|
||
|
// key is explicitly declared to be undefined or not.
|
||
|
if (maybeKey !== undefined) {
|
||
|
if (__DEV__) {
|
||
|
maybeKey |> checkKeyStringCoercion(%);
|
||
|
}
|
||
|
key = '' + maybeKey;
|
||
|
}
|
||
|
if (config |> hasValidKey(%)) {
|
||
|
if (__DEV__) {
|
||
|
config.key |> checkKeyStringCoercion(%);
|
||
|
}
|
||
|
key = '' + config.key;
|
||
|
}
|
||
|
if (config |> hasValidRef(%)) {
|
||
|
if (!enableRefAsProp) {
|
||
|
ref = config.ref;
|
||
|
if (!disableStringRefs) {
|
||
|
ref = coerceStringRef(ref, getOwner(), type);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
let props;
|
||
|
if ((enableFastJSXWithoutStringRefs || enableFastJSXWithStringRefs && !('ref' in config)) && !('key' in config)) {
|
||
|
// If key was not spread in, we can reuse the original props object. This
|
||
|
// only works for `jsx`, not `createElement`, because `jsx` is a compiler
|
||
|
// target and the compiler always passes a new object. For `createElement`,
|
||
|
// we can't assume a new object is passed every time because it can be
|
||
|
// called manually.
|
||
|
//
|
||
|
// Spreading key is a warning in dev. In a future release, we will not
|
||
|
// remove a spread key from the props object. (But we'll still warn.) We'll
|
||
|
// always pass the object straight through.
|
||
|
props = config;
|
||
|
} else {
|
||
|
// We need to remove reserved props (key, prop, ref). Create a fresh props
|
||
|
// object and copy over all the non-reserved props. We don't use `delete`
|
||
|
// because in V8 it will deopt the object to dictionary mode.
|
||
|
props = {};
|
||
|
for (const propName in config) {
|
||
|
// Skip over reserved prop names
|
||
|
if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
|
||
|
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
|
||
|
props.ref = coerceStringRef(config[propName], getOwner(), type);
|
||
|
} else {
|
||
|
props[propName] = config[propName];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!disableDefaultPropsExceptForClasses) {
|
||
|
// Resolve default props
|
||
|
if (type && type.defaultProps) {
|
||
|
const defaultProps = type.defaultProps;
|
||
|
for (const propName in defaultProps) {
|
||
|
if (props[propName] === undefined) {
|
||
|
props[propName] = defaultProps[propName];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return ReactElement(type, key, ref, undefined, undefined, getOwner(), props, undefined, undefined);
|
||
|
}
|
||
|
|
||
|
// While `jsxDEV` should never be called when running in production, we do
|
||
|
// support `jsx` and `jsxs` when running in development. This supports the case
|
||
|
// where a third-party dependency ships code that was compiled for production;
|
||
|
// we want to still provide warnings in development.
|
||
|
//
|
||
|
// So these functions are the _dev_ implementations of the _production_
|
||
|
// API signatures.
|
||
|
//
|
||
|
// Since these functions are dev-only, it's ok to add an indirection here. They
|
||
|
// only exist to provide different versions of `isStaticChildren`. (We shouldn't
|
||
|
// use this pattern for the prod versions, though, because it will add an call
|
||
|
// frame.)
|
||
|
export function jsxProdSignatureRunningInDevWithDynamicChildren(type, config, maybeKey, source, self) {
|
||
|
if (__DEV__) {
|
||
|
const isStaticChildren = false;
|
||
|
return jsxDEV(type, config, maybeKey, isStaticChildren, source, self);
|
||
|
}
|
||
|
}
|
||
|
export function jsxProdSignatureRunningInDevWithStaticChildren(type, config, maybeKey, source, self) {
|
||
|
if (__DEV__) {
|
||
|
const isStaticChildren = true;
|
||
|
return jsxDEV(type, config, maybeKey, isStaticChildren, source, self);
|
||
|
}
|
||
|
}
|
||
|
const didWarnAboutKeySpread = {};
|
||
|
|
||
|
/**
|
||
|
* https://github.com/reactjs/rfcs/pull/107
|
||
|
* @param {*} type
|
||
|
* @param {object} props
|
||
|
* @param {string} key
|
||
|
*/
|
||
|
export function jsxDEV(type, config, maybeKey, isStaticChildren, source, self) {
|
||
|
if (__DEV__) {
|
||
|
if (!(type |> isValidElementType(%))) {
|
||
|
// This is an invalid element type.
|
||
|
//
|
||
|
// We warn in this case but don't throw. We expect the element creation to
|
||
|
// succeed and there will likely be errors in render.
|
||
|
let info = '';
|
||
|
if (type === undefined || typeof type === 'object' && type !== null && (type |> Object.keys(%)).length === 0) {
|
||
|
info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports.";
|
||
|
}
|
||
|
let typeString;
|
||
|
if (type === null) {
|
||
|
typeString = 'null';
|
||
|
} else if (type |> isArray(%)) {
|
||
|
typeString = 'array';
|
||
|
} else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
|
||
|
typeString = `<${type.type |> getComponentNameFromType(%) || 'Unknown'} />`;
|
||
|
info = ' Did you accidentally export a JSX literal instead of a component?';
|
||
|
} else {
|
||
|
typeString = typeof type;
|
||
|
}
|
||
|
console.error('React.jsx: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info);
|
||
|
} else {
|
||
|
// This is a valid element type.
|
||
|
|
||
|
// Skip key warning if the type isn't valid since our key validation logic
|
||
|
// doesn't expect a non-string/function type and can throw confusing
|
||
|
// errors. We don't want exception behavior to differ between dev and
|
||
|
// prod. (Rendering will throw with a helpful message and as soon as the
|
||
|
// type is fixed, the key warnings will appear.)
|
||
|
const children = config.children;
|
||
|
if (children !== undefined) {
|
||
|
if (isStaticChildren) {
|
||
|
if (children |> isArray(%)) {
|
||
|
for (let i = 0; i < children.length; i++) {
|
||
|
children[i] |> validateChildKeys(%, type);
|
||
|
}
|
||
|
if (Object.freeze) {
|
||
|
children |> Object.freeze(%);
|
||
|
}
|
||
|
} else {
|
||
|
'React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + 'Use the Babel transform instead.' |> console.error(%);
|
||
|
}
|
||
|
} else {
|
||
|
children |> validateChildKeys(%, type);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Warn about key spread regardless of whether the type is valid.
|
||
|
if (config |> hasOwnProperty.call(%, 'key')) {
|
||
|
const componentName = type |> getComponentNameFromType(%);
|
||
|
const keys = (k => k !== 'key') |> (config |> Object.keys(%)).filter(%);
|
||
|
const beforeExample = keys.length > 0 ? '{key: someKey, ' + (': ..., ' |> keys.join(%)) + ': ...}' : '{key: someKey}';
|
||
|
if (!didWarnAboutKeySpread[componentName + beforeExample]) {
|
||
|
const afterExample = keys.length > 0 ? '{' + (': ..., ' |> keys.join(%)) + ': ...}' : '{}';
|
||
|
console.error('A props object containing a "key" prop is being spread into JSX:\n' + ' let props = %s;\n' + ' <%s {...props} />\n' + 'React keys must be passed directly to JSX without using spread:\n' + ' let props = %s;\n' + ' <%s key={someKey} {...props} />', beforeExample, componentName, afterExample, componentName);
|
||
|
didWarnAboutKeySpread[componentName + beforeExample] = true;
|
||
|
}
|
||
|
}
|
||
|
let key = null;
|
||
|
let ref = null;
|
||
|
|
||
|
// Currently, key can be spread in as a prop. This causes a potential
|
||
|
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
|
||
|
// or <div key="Hi" {...props} /> ). We want to deprecate key spread,
|
||
|
// but as an intermediary step, we will use jsxDEV for everything except
|
||
|
// <div {...props} key="Hi" />, because we aren't currently able to tell if
|
||
|
// key is explicitly declared to be undefined or not.
|
||
|
if (maybeKey !== undefined) {
|
||
|
if (__DEV__) {
|
||
|
maybeKey |> checkKeyStringCoercion(%);
|
||
|
}
|
||
|
key = '' + maybeKey;
|
||
|
}
|
||
|
if (config |> hasValidKey(%)) {
|
||
|
if (__DEV__) {
|
||
|
config.key |> checkKeyStringCoercion(%);
|
||
|
}
|
||
|
key = '' + config.key;
|
||
|
}
|
||
|
if (config |> hasValidRef(%)) {
|
||
|
if (!enableRefAsProp) {
|
||
|
ref = config.ref;
|
||
|
if (!disableStringRefs) {
|
||
|
ref = coerceStringRef(ref, getOwner(), type);
|
||
|
}
|
||
|
}
|
||
|
if (!disableStringRefs) {
|
||
|
config |> warnIfStringRefCannotBeAutoConverted(%, self);
|
||
|
}
|
||
|
}
|
||
|
let props;
|
||
|
if ((enableFastJSXWithoutStringRefs || enableFastJSXWithStringRefs && !('ref' in config)) && !('key' in config)) {
|
||
|
// If key was not spread in, we can reuse the original props object. This
|
||
|
// only works for `jsx`, not `createElement`, because `jsx` is a compiler
|
||
|
// target and the compiler always passes a new object. For `createElement`,
|
||
|
// we can't assume a new object is passed every time because it can be
|
||
|
// called manually.
|
||
|
//
|
||
|
// Spreading key is a warning in dev. In a future release, we will not
|
||
|
// remove a spread key from the props object. (But we'll still warn.) We'll
|
||
|
// always pass the object straight through.
|
||
|
props = config;
|
||
|
} else {
|
||
|
// We need to remove reserved props (key, prop, ref). Create a fresh props
|
||
|
// object and copy over all the non-reserved props. We don't use `delete`
|
||
|
// because in V8 it will deopt the object to dictionary mode.
|
||
|
props = {};
|
||
|
for (const propName in config) {
|
||
|
// Skip over reserved prop names
|
||
|
if (propName !== 'key' && (enableRefAsProp || propName !== 'ref')) {
|
||
|
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
|
||
|
props.ref = coerceStringRef(config[propName], getOwner(), type);
|
||
|
} else {
|
||
|
props[propName] = config[propName];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (!disableDefaultPropsExceptForClasses) {
|
||
|
// Resolve default props
|
||
|
if (type && type.defaultProps) {
|
||
|
const defaultProps = type.defaultProps;
|
||
|
for (const propName in defaultProps) {
|
||
|
if (props[propName] === undefined) {
|
||
|
props[propName] = defaultProps[propName];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (key || !enableRefAsProp && ref) {
|
||
|
const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
|
||
|
if (key) {
|
||
|
props |> defineKeyPropWarningGetter(%, displayName);
|
||
|
}
|
||
|
if (!enableRefAsProp && ref) {
|
||
|
props |> defineRefPropWarningGetter(%, displayName);
|
||
|
}
|
||
|
}
|
||
|
const element = ReactElement(type, key, ref, self, source, getOwner(), props, __DEV__ && enableOwnerStacks ? 'react-stack-top-frame' |> Error(%) : undefined, __DEV__ && enableOwnerStacks ? type |> getTaskName(%) |> createTask(%) : undefined);
|
||
|
if (type === REACT_FRAGMENT_TYPE) {
|
||
|
element |> validateFragmentProps(%);
|
||
|
}
|
||
|
return element;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create and return a new ReactElement of the given type.
|
||
|
* See https://reactjs.org/docs/react-api.html#createelement
|
||
|
*/
|
||
|
export function createElement(type, config, children) {
|
||
|
if (__DEV__) {
|
||
|
if (!(type |> isValidElementType(%))) {
|
||
|
// This is an invalid element type.
|
||
|
//
|
||
|
// We warn in this case but don't throw. We expect the element creation to
|
||
|
// succeed and there will likely be errors in render.
|
||
|
let info = '';
|
||
|
if (type === undefined || typeof type === 'object' && type !== null && (type |> Object.keys(%)).length === 0) {
|
||
|
info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports.";
|
||
|
}
|
||
|
let typeString;
|
||
|
if (type === null) {
|
||
|
typeString = 'null';
|
||
|
} else if (type |> isArray(%)) {
|
||
|
typeString = 'array';
|
||
|
} else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
|
||
|
typeString = `<${type.type |> getComponentNameFromType(%) || 'Unknown'} />`;
|
||
|
info = ' Did you accidentally export a JSX literal instead of a component?';
|
||
|
} else {
|
||
|
typeString = typeof type;
|
||
|
}
|
||
|
console.error('React.createElement: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info);
|
||
|
} else {
|
||
|
// This is a valid element type.
|
||
|
|
||
|
// Skip key warning if the type isn't valid since our key validation logic
|
||
|
// doesn't expect a non-string/function type and can throw confusing
|
||
|
// errors. We don't want exception behavior to differ between dev and
|
||
|
// prod. (Rendering will throw with a helpful message and as soon as the
|
||
|
// type is fixed, the key warnings will appear.)
|
||
|
for (let i = 2; i < arguments.length; i++) {
|
||
|
arguments[i] |> validateChildKeys(%, type);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Unlike the jsx() runtime, createElement() doesn't warn about key spread.
|
||
|
}
|
||
|
let propName;
|
||
|
|
||
|
// Reserved names are extracted
|
||
|
const props = {};
|
||
|
let key = null;
|
||
|
let ref = null;
|
||
|
if (config != null) {
|
||
|
if (__DEV__) {
|
||
|
if (!didWarnAboutOldJSXRuntime && '__self' in config &&
|
||
|
// Do not assume this is the result of an oudated JSX transform if key
|
||
|
// is present, because the modern JSX transform sometimes outputs
|
||
|
// createElement to preserve precedence between a static key and a
|
||
|
// spread key. To avoid false positive warnings, we never warn if
|
||
|
// there's a key.
|
||
|
!('key' in config)) {
|
||
|
didWarnAboutOldJSXRuntime = true;
|
||
|
'Your app (or one of its dependencies) is using an outdated JSX ' + 'transform. Update to the modern JSX transform for ' + 'faster performance: https://react.dev/link/new-jsx-transform' |> console.warn(%);
|
||
|
}
|
||
|
}
|
||
|
if (config |> hasValidRef(%)) {
|
||
|
if (!enableRefAsProp) {
|
||
|
ref = config.ref;
|
||
|
if (!disableStringRefs) {
|
||
|
ref = coerceStringRef(ref, getOwner(), type);
|
||
|
}
|
||
|
}
|
||
|
if (__DEV__ && !disableStringRefs) {
|
||
|
config |> warnIfStringRefCannotBeAutoConverted(%, config.__self);
|
||
|
}
|
||
|
}
|
||
|
if (config |> hasValidKey(%)) {
|
||
|
if (__DEV__) {
|
||
|
config.key |> checkKeyStringCoercion(%);
|
||
|
}
|
||
|
key = '' + config.key;
|
||
|
}
|
||
|
|
||
|
// Remaining properties are added to a new props object
|
||
|
for (propName in config) {
|
||
|
if ((config |> hasOwnProperty.call(%, propName)) &&
|
||
|
// Skip over reserved prop names
|
||
|
propName !== 'key' && (enableRefAsProp || propName !== 'ref') &&
|
||
|
// Even though we don't use these anymore in the runtime, we don't want
|
||
|
// them to appear as props, so in createElement we filter them out.
|
||
|
// We don't have to do this in the jsx() runtime because the jsx()
|
||
|
// transform never passed these as props; it used separate arguments.
|
||
|
propName !== '__self' && propName !== '__source') {
|
||
|
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
|
||
|
props.ref = coerceStringRef(config[propName], getOwner(), type);
|
||
|
} else {
|
||
|
props[propName] = config[propName];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Children can be more than one argument, and those are transferred onto
|
||
|
// the newly allocated props object.
|
||
|
const childrenLength = arguments.length - 2;
|
||
|
if (childrenLength === 1) {
|
||
|
props.children = children;
|
||
|
} else if (childrenLength > 1) {
|
||
|
const childArray = childrenLength |> Array(%);
|
||
|
for (let i = 0; i < childrenLength; i++) {
|
||
|
childArray[i] = arguments[i + 2];
|
||
|
}
|
||
|
if (__DEV__) {
|
||
|
if (Object.freeze) {
|
||
|
childArray |> Object.freeze(%);
|
||
|
}
|
||
|
}
|
||
|
props.children = childArray;
|
||
|
}
|
||
|
|
||
|
// Resolve default props
|
||
|
if (type && type.defaultProps) {
|
||
|
const defaultProps = type.defaultProps;
|
||
|
for (propName in defaultProps) {
|
||
|
if (props[propName] === undefined) {
|
||
|
props[propName] = defaultProps[propName];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (__DEV__) {
|
||
|
if (key || !enableRefAsProp && ref) {
|
||
|
const displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
|
||
|
if (key) {
|
||
|
props |> defineKeyPropWarningGetter(%, displayName);
|
||
|
}
|
||
|
if (!enableRefAsProp && ref) {
|
||
|
props |> defineRefPropWarningGetter(%, displayName);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
const element = ReactElement(type, key, ref, undefined, undefined, getOwner(), props, __DEV__ && enableOwnerStacks ? 'react-stack-top-frame' |> Error(%) : undefined, __DEV__ && enableOwnerStacks ? type |> getTaskName(%) |> createTask(%) : undefined);
|
||
|
if (type === REACT_FRAGMENT_TYPE) {
|
||
|
element |> validateFragmentProps(%);
|
||
|
}
|
||
|
return element;
|
||
|
}
|
||
|
export function cloneAndReplaceKey(oldElement, newKey) {
|
||
|
return ReactElement(oldElement.type, newKey,
|
||
|
// When enableRefAsProp is on, this argument is ignored. This check only
|
||
|
// exists to avoid the `ref` access warning.
|
||
|
enableRefAsProp ? null : oldElement.ref, undefined, undefined, !__DEV__ && disableStringRefs ? undefined : oldElement._owner, oldElement.props, __DEV__ && enableOwnerStacks ? oldElement._debugStack : undefined, __DEV__ && enableOwnerStacks ? oldElement._debugTask : undefined);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clone and return a new ReactElement using element as the starting point.
|
||
|
* See https://reactjs.org/docs/react-api.html#cloneelement
|
||
|
*/
|
||
|
export function cloneElement(element, config, children) {
|
||
|
if (element === null || element === undefined) {
|
||
|
throw new Error(`The argument must be a React element, but you passed ${element}.`);
|
||
|
}
|
||
|
let propName;
|
||
|
|
||
|
// Original props are copied
|
||
|
const props = {} |> assign(%, element.props);
|
||
|
|
||
|
// Reserved names are extracted
|
||
|
let key = element.key;
|
||
|
let ref = enableRefAsProp ? null : element.ref;
|
||
|
|
||
|
// Owner will be preserved, unless ref is overridden
|
||
|
let owner = !__DEV__ && disableStringRefs ? undefined : element._owner;
|
||
|
if (config != null) {
|
||
|
if (config |> hasValidRef(%)) {
|
||
|
owner = __DEV__ || !disableStringRefs ? getOwner() : undefined;
|
||
|
if (!enableRefAsProp) {
|
||
|
// Silently steal the ref from the parent.
|
||
|
ref = config.ref;
|
||
|
if (!disableStringRefs) {
|
||
|
ref = coerceStringRef(ref, owner, element.type);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (config |> hasValidKey(%)) {
|
||
|
if (__DEV__) {
|
||
|
config.key |> checkKeyStringCoercion(%);
|
||
|
}
|
||
|
key = '' + config.key;
|
||
|
}
|
||
|
|
||
|
// Remaining properties override existing props
|
||
|
let defaultProps;
|
||
|
if (!disableDefaultPropsExceptForClasses && element.type && element.type.defaultProps) {
|
||
|
defaultProps = element.type.defaultProps;
|
||
|
}
|
||
|
for (propName in config) {
|
||
|
if ((config |> hasOwnProperty.call(%, propName)) &&
|
||
|
// Skip over reserved prop names
|
||
|
propName !== 'key' && (enableRefAsProp || propName !== 'ref') &&
|
||
|
// ...and maybe these, too, though we currently rely on them for
|
||
|
// warnings and debug information in dev. Need to decide if we're OK
|
||
|
// with dropping them. In the jsx() runtime it's not an issue because
|
||
|
// the data gets passed as separate arguments instead of props, but
|
||
|
// it would be nice to stop relying on them entirely so we can drop
|
||
|
// them from the internal Fiber field.
|
||
|
propName !== '__self' && propName !== '__source' &&
|
||
|
// Undefined `ref` is ignored by cloneElement. We treat it the same as
|
||
|
// if the property were missing. This is mostly for
|
||
|
// backwards compatibility.
|
||
|
!(enableRefAsProp && propName === 'ref' && config.ref === undefined)) {
|
||
|
if (!disableDefaultPropsExceptForClasses && config[propName] === undefined && defaultProps !== undefined) {
|
||
|
// Resolve default props
|
||
|
props[propName] = defaultProps[propName];
|
||
|
} else {
|
||
|
if (enableRefAsProp && !disableStringRefs && propName === 'ref') {
|
||
|
props.ref = coerceStringRef(config[propName], owner, element.type);
|
||
|
} else {
|
||
|
props[propName] = config[propName];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Children can be more than one argument, and those are transferred onto
|
||
|
// the newly allocated props object.
|
||
|
const childrenLength = arguments.length - 2;
|
||
|
if (childrenLength === 1) {
|
||
|
props.children = children;
|
||
|
} else if (childrenLength > 1) {
|
||
|
const childArray = childrenLength |> Array(%);
|
||
|
for (let i = 0; i < childrenLength; i++) {
|
||
|
childArray[i] = arguments[i + 2];
|
||
|
}
|
||
|
props.children = childArray;
|
||
|
}
|
||
|
const clonedElement = ReactElement(element.type, key, ref, undefined, undefined, owner, props, __DEV__ && enableOwnerStacks ? element._debugStack : undefined, __DEV__ && enableOwnerStacks ? element._debugTask : undefined);
|
||
|
for (let i = 2; i < arguments.length; i++) {
|
||
|
arguments[i] |> validateChildKeys(%, clonedElement.type);
|
||
|
}
|
||
|
return clonedElement;
|
||
|
}
|
||
|
function getDeclarationErrorAddendum() {
|
||
|
if (__DEV__) {
|
||
|
const owner = getOwner();
|
||
|
if (owner) {
|
||
|
const name = owner.type |> getComponentNameFromType(%);
|
||
|
if (name) {
|
||
|
return '\n\nCheck the render method of `' + name + '`.';
|
||
|
}
|
||
|
}
|
||
|
return '';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Ensure that every element either is passed in a static location, in an
|
||
|
* array with an explicit keys property defined, or in an object literal
|
||
|
* with valid key property.
|
||
|
*
|
||
|
* @internal
|
||
|
* @param {ReactNode} node Statically passed child of any type.
|
||
|
* @param {*} parentType node's parent's type.
|
||
|
*/
|
||
|
function validateChildKeys(node, parentType) {
|
||
|
if (__DEV__) {
|
||
|
if (typeof node !== 'object' || !node) {
|
||
|
return;
|
||
|
}
|
||
|
if (node.$$typeof === REACT_CLIENT_REFERENCE) {
|
||
|
// This is a reference to a client component so it's unknown.
|
||
|
} else if (node |> isArray(%)) {
|
||
|
for (let i = 0; i < node.length; i++) {
|
||
|
const child = node[i];
|
||
|
if (child |> isValidElement(%)) {
|
||
|
child |> validateExplicitKey(%, parentType);
|
||
|
}
|
||
|
}
|
||
|
} else if (node |> isValidElement(%)) {
|
||
|
// This element was passed in a valid location.
|
||
|
if (node._store) {
|
||
|
node._store.validated = true;
|
||
|
}
|
||
|
} else {
|
||
|
const iteratorFn = node |> getIteratorFn(%);
|
||
|
if (typeof iteratorFn === 'function') {
|
||
|
// Entry iterators used to provide implicit keys,
|
||
|
// but now we print a separate warning for them later.
|
||
|
if (iteratorFn !== node.entries) {
|
||
|
const iterator = node |> iteratorFn.call(%);
|
||
|
if (iterator !== node) {
|
||
|
let step;
|
||
|
while (!(step = iterator.next()).done) {
|
||
|
if (step.value |> isValidElement(%)) {
|
||
|
step.value |> validateExplicitKey(%, parentType);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Verifies the object is a ReactElement.
|
||
|
* See https://reactjs.org/docs/react-api.html#isvalidelement
|
||
|
* @param {?object} object
|
||
|
* @return {boolean} True if `object` is a ReactElement.
|
||
|
* @final
|
||
|
*/
|
||
|
export function isValidElement(object) {
|
||
|
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
|
||
|
}
|
||
|
const ownerHasKeyUseWarning = {};
|
||
|
|
||
|
/**
|
||
|
* Warn if the element doesn't have an explicit key assigned to it.
|
||
|
* This element is in an array. The array could grow and shrink or be
|
||
|
* reordered. All children that haven't already been validated are required to
|
||
|
* have a "key" property assigned to it. Error statuses are cached so a warning
|
||
|
* will only be shown once.
|
||
|
*
|
||
|
* @internal
|
||
|
* @param {ReactElement} element Element that requires a key.
|
||
|
* @param {*} parentType element's parent's type.
|
||
|
*/
|
||
|
function validateExplicitKey(element, parentType) {
|
||
|
if (__DEV__) {
|
||
|
if (!element._store || element._store.validated || element.key != null) {
|
||
|
return;
|
||
|
}
|
||
|
element._store.validated = true;
|
||
|
const currentComponentErrorInfo = parentType |> getCurrentComponentErrorInfo(%);
|
||
|
if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
|
||
|
return;
|
||
|
}
|
||
|
ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
|
||
|
|
||
|
// Usually the current owner is the offender, but if it accepts children as a
|
||
|
// property, it may be the creator of the child that's responsible for
|
||
|
// assigning it a key.
|
||
|
let childOwner = '';
|
||
|
if (element && element._owner != null && element._owner !== getOwner()) {
|
||
|
let ownerName = null;
|
||
|
if (typeof element._owner.tag === 'number') {
|
||
|
ownerName = element._owner.type |> getComponentNameFromType(%);
|
||
|
} else if (typeof element._owner.name === 'string') {
|
||
|
ownerName = element._owner.name;
|
||
|
}
|
||
|
// Give the component that originally created this child.
|
||
|
childOwner = ` It was passed a child from ${ownerName}.`;
|
||
|
}
|
||
|
element |> setCurrentlyValidatingElement(%);
|
||
|
console.error('Each child in a list should have a unique "key" prop.' + '%s%s See https://react.dev/link/warning-keys for more information.', currentComponentErrorInfo, childOwner);
|
||
|
null |> setCurrentlyValidatingElement(%);
|
||
|
}
|
||
|
}
|
||
|
function setCurrentlyValidatingElement(element) {
|
||
|
if (__DEV__) {
|
||
|
if (element) {
|
||
|
const owner = element._owner;
|
||
|
const stack = element.type |> describeUnknownElementTypeFrameInDEV(%, owner ? owner.type : null);
|
||
|
stack |> ReactSharedInternals.setExtraStackFrame(%);
|
||
|
} else {
|
||
|
null |> ReactSharedInternals.setExtraStackFrame(%);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function getCurrentComponentErrorInfo(parentType) {
|
||
|
if (__DEV__) {
|
||
|
let info = getDeclarationErrorAddendum();
|
||
|
if (!info) {
|
||
|
const parentName = parentType |> getComponentNameFromType(%);
|
||
|
if (parentName) {
|
||
|
info = `\n\nCheck the top-level render call using <${parentName}>.`;
|
||
|
}
|
||
|
}
|
||
|
return info;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Given a fragment, validate that it can only be provided with fragment props
|
||
|
* @param {ReactElement} fragment
|
||
|
*/
|
||
|
function validateFragmentProps(fragment) {
|
||
|
// TODO: Move this to render phase instead of at element creation.
|
||
|
if (__DEV__) {
|
||
|
const keys = fragment.props |> Object.keys(%);
|
||
|
for (let i = 0; i < keys.length; i++) {
|
||
|
const key = keys[i];
|
||
|
if (key !== 'children' && key !== 'key') {
|
||
|
fragment |> setCurrentlyValidatingElement(%);
|
||
|
'Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.' |> console.error(%, key);
|
||
|
null |> setCurrentlyValidatingElement(%);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!enableRefAsProp && fragment.ref !== null) {
|
||
|
fragment |> setCurrentlyValidatingElement(%);
|
||
|
'Invalid attribute `ref` supplied to `React.Fragment`.' |> console.error(%);
|
||
|
null |> setCurrentlyValidatingElement(%);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
function coerceStringRef(mixedRef, owner, type) {
|
||
|
if (disableStringRefs) {
|
||
|
return mixedRef;
|
||
|
}
|
||
|
let stringRef;
|
||
|
if (typeof mixedRef === 'string') {
|
||
|
stringRef = mixedRef;
|
||
|
} else {
|
||
|
if (typeof mixedRef === 'number' || typeof mixedRef === 'boolean') {
|
||
|
if (__DEV__) {
|
||
|
mixedRef |> checkPropStringCoercion(%, 'ref');
|
||
|
}
|
||
|
stringRef = '' + mixedRef;
|
||
|
} else {
|
||
|
return mixedRef;
|
||
|
}
|
||
|
}
|
||
|
const callback = stringRefAsCallbackRef.bind(null, stringRef, type, owner);
|
||
|
// This is used to check whether two callback refs conceptually represent
|
||
|
// the same string ref, and can therefore be reused by the reconciler. Needed
|
||
|
// for backwards compatibility with old Meta code that relies on string refs
|
||
|
// not being reattached on every render.
|
||
|
callback.__stringRef = stringRef;
|
||
|
callback.__type = type;
|
||
|
callback.__owner = owner;
|
||
|
return callback;
|
||
|
}
|
||
|
function stringRefAsCallbackRef(stringRef, type, owner, value) {
|
||
|
if (disableStringRefs) {
|
||
|
return;
|
||
|
}
|
||
|
if (!owner) {
|
||
|
throw new Error(`Element ref was specified as a string (${stringRef}) but no owner was set. This could happen for one of` + ' the following reasons:\n' + '1. You may be adding a ref to a function component\n' + "2. You may be adding a ref to a component that was not created inside a component's render method\n" + '3. You have multiple copies of React loaded\n' + 'See https://react.dev/link/refs-must-have-owner for more information.');
|
||
|
}
|
||
|
if (owner.tag !== ClassComponent) {
|
||
|
throw new Error('Function components cannot have string refs. ' + 'We recommend using useRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://react.dev/link/strict-mode-string-ref');
|
||
|
}
|
||
|
if (__DEV__) {
|
||
|
if (
|
||
|
// Will already warn with "Function components cannot be given refs"
|
||
|
!(typeof type === 'function' && !(type |> isReactClass(%)))) {
|
||
|
const componentName = owner |> getComponentNameFromFiber(%) || 'Component';
|
||
|
if (!didWarnAboutStringRefs[componentName]) {
|
||
|
console.error('Component "%s" contains the string ref "%s". Support for string refs ' + 'will be removed in a future major release. We recommend using ' + 'useRef() or createRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://react.dev/link/strict-mode-string-ref', componentName, stringRef);
|
||
|
didWarnAboutStringRefs[componentName] = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
const inst = owner.stateNode;
|
||
|
if (!inst) {
|
||
|
throw new Error(`Missing owner for string ref ${stringRef}. This error is likely caused by a ` + 'bug in React. Please file an issue.');
|
||
|
}
|
||
|
const refs = inst.refs;
|
||
|
if (value === null) {
|
||
|
delete refs[stringRef];
|
||
|
} else {
|
||
|
refs[stringRef] = value;
|
||
|
}
|
||
|
}
|
||
|
function isReactClass(type) {
|
||
|
return type.prototype && type.prototype.isReactComponent;
|
||
|
}
|