diff --git a/packages/react/index.classic.fb.js b/packages/react/index.classic.fb.js index 035e5c0aec2d9..a417f3e755cab 100644 --- a/packages/react/index.classic.fb.js +++ b/packages/react/index.classic.fb.js @@ -58,5 +58,5 @@ export { useSyncExternalStore, useTransition, version, -} from './src/React'; +} from './src/ReactClient'; export {jsx, jsxs, jsxDEV} from './src/jsx/ReactJSX'; diff --git a/packages/react/index.experimental.js b/packages/react/index.experimental.js index bdb26a4c42076..dd86090f093e1 100644 --- a/packages/react/index.experimental.js +++ b/packages/react/index.experimental.js @@ -57,9 +57,9 @@ export { useSyncExternalStore, useTransition, version, -} from './src/React'; +} from './src/ReactClient'; -import {useOptimistic} from './src/React'; +import {useOptimistic} from './src/ReactClient'; export function experimental_useOptimistic( passthrough: S, diff --git a/packages/react/index.js b/packages/react/index.js index b60e8957c5b0a..abce6537b5675 100644 --- a/packages/react/index.js +++ b/packages/react/index.js @@ -80,4 +80,4 @@ export { useState, useTransition, version, -} from './src/React'; +} from './src/ReactClient'; diff --git a/packages/react/index.modern.fb.js b/packages/react/index.modern.fb.js index 1ef63b605af3a..10ae150f64eef 100644 --- a/packages/react/index.modern.fb.js +++ b/packages/react/index.modern.fb.js @@ -56,5 +56,5 @@ export { useSyncExternalStore, useTransition, version, -} from './src/React'; +} from './src/ReactClient'; export {jsx, jsxs, jsxDEV} from './src/jsx/ReactJSX'; diff --git a/packages/react/index.stable.js b/packages/react/index.stable.js index b88a83d78b9b2..9f8e46063782a 100644 --- a/packages/react/index.stable.js +++ b/packages/react/index.stable.js @@ -47,4 +47,4 @@ export { useSyncExternalStore, useTransition, version, -} from './src/React'; +} from './src/ReactClient'; diff --git a/packages/react/src/React.js b/packages/react/src/ReactClient.js similarity index 84% rename from packages/react/src/React.js rename to packages/react/src/ReactClient.js index 5728a2ba50d78..5aaae5aaf8e37 100644 --- a/packages/react/src/React.js +++ b/packages/react/src/ReactClient.js @@ -26,9 +26,9 @@ import {Component, PureComponent} from './ReactBaseClasses'; import {createRef} from './ReactCreateRef'; import {forEach, map, count, toArray, only} from './ReactChildren'; import { - createElement as createElementProd, - createFactory as createFactoryProd, - cloneElement as cloneElementProd, + createElement, + createFactory, + cloneElement, isValidElement, } from './ReactElement'; import {createContext} from './ReactContext'; @@ -61,27 +61,12 @@ import { useMemoCache, useOptimistic, } from './ReactHooks'; -import { - createElementWithValidation, - createFactoryWithValidation, - cloneElementWithValidation, -} from './ReactElementValidator'; + import {createServerContext} from './ReactServerContext'; import ReactSharedInternals from './ReactSharedInternalsClient'; import {startTransition} from './ReactStartTransition'; import {act} from './ReactAct'; -// TODO: Move this branching into the other module instead and just re-export. -const createElement: any = __DEV__ - ? createElementWithValidation - : createElementProd; -const cloneElement: any = __DEV__ - ? cloneElementWithValidation - : cloneElementProd; -const createFactory: any = __DEV__ - ? createFactoryWithValidation - : createFactoryProd; - const Children = { map, forEach, diff --git a/packages/react/src/ReactElement.js b/packages/react/src/ReactElement.js index e9d721b92693a..ab3df9600ab86 100644 --- a/packages/react/src/ReactElement.js +++ b/packages/react/src/ReactElement.js @@ -3,571 +3,30 @@ * * 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 {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; -import assign from 'shared/assign'; -import hasOwnProperty from 'shared/hasOwnProperty'; -import {checkKeyStringCoercion} from 'shared/CheckStringCoercion'; - -import ReactCurrentOwner from './ReactCurrentOwner'; - -const RESERVED_PROPS = { - key: true, - ref: true, - __self: true, - __source: true, -}; - -let specialPropKeyWarningShown, - specialPropRefWarningShown, - didWarnAboutStringRefs; - -if (__DEV__) { - didWarnAboutStringRefs = {}; -} - -function hasValidRef(config) { - if (__DEV__) { - if (hasOwnProperty.call(config, 'ref')) { - const getter = Object.getOwnPropertyDescriptor(config, 'ref').get; - if (getter && getter.isReactWarning) { - return false; - } - } - } - return config.ref !== undefined; -} - -function hasValidKey(config) { - if (__DEV__) { - if (hasOwnProperty.call(config, 'key')) { - const getter = Object.getOwnPropertyDescriptor(config, 'key').get; - if (getter && getter.isReactWarning) { - return false; - } - } - } - return config.key !== undefined; -} - -function defineKeyPropWarningGetter(props, displayName) { - const warnAboutAccessingKey = function () { - if (__DEV__) { - if (!specialPropKeyWarningShown) { - specialPropKeyWarningShown = true; - console.error( - '%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://reactjs.org/link/special-props)', - displayName, - ); - } - } - }; - warnAboutAccessingKey.isReactWarning = true; - Object.defineProperty(props, 'key', { - get: warnAboutAccessingKey, - configurable: true, - }); -} - -function defineRefPropWarningGetter(props, displayName) { - const warnAboutAccessingRef = function () { - if (__DEV__) { - if (!specialPropRefWarningShown) { - specialPropRefWarningShown = true; - console.error( - '%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://reactjs.org/link/special-props)', - displayName, - ); - } - } - }; - warnAboutAccessingRef.isReactWarning = true; - Object.defineProperty(props, 'ref', { - get: warnAboutAccessingRef, - configurable: true, - }); -} - -function warnIfStringRefCannotBeAutoConverted(config) { - if (__DEV__) { - if ( - typeof config.ref === 'string' && - ReactCurrentOwner.current && - config.__self && - ReactCurrentOwner.current.stateNode !== config.__self - ) { - const componentName = getComponentNameFromType( - ReactCurrentOwner.current.type, - ); - - 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://reactjs.org/link/strict-mode-string-ref', - componentName, - config.ref, - ); - didWarnAboutStringRefs[componentName] = true; - } - } - } -} - -/** - * 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.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) { - const 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: type, - key: key, - ref: ref, - props: 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, - }); - // self and source are DEV only properties. - Object.defineProperty(element, '_self', { - configurable: false, - enumerable: false, - writable: false, - value: self, - }); - // Two elements created in two different places should be considered - // equal for testing purposes and therefore we hide it from enumeration. - Object.defineProperty(element, '_source', { - configurable: false, - enumerable: false, - writable: false, - value: source, - }); - if (Object.freeze) { - Object.freeze(element.props); - Object.freeze(element); - } - } - - return element; -} - -/** - * https://github.com/reactjs/rfcs/pull/107 - * @param {*} type - * @param {object} props - * @param {string} key - */ -export function jsx(type, config, maybeKey) { - let propName; - - // Reserved names are extracted - const props = {}; - - 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.
- // or
). We want to deprecate key spread, - // but as an intermediary step, we will use jsxDEV for everything except - //
, because we aren't currently able to tell if - // key is explicitly declared to be undefined or not. - if (maybeKey !== undefined) { - if (__DEV__) { - checkKeyStringCoercion(maybeKey); - } - key = '' + maybeKey; - } - - if (hasValidKey(config)) { - if (__DEV__) { - checkKeyStringCoercion(config.key); - } - key = '' + config.key; - } - - if (hasValidRef(config)) { - ref = config.ref; - } - - // Remaining properties are added to a new props object - for (propName in config) { - if ( - hasOwnProperty.call(config, propName) && - !RESERVED_PROPS.hasOwnProperty(propName) - ) { - props[propName] = config[propName]; - } - } - - // Resolve default props - if (type && type.defaultProps) { - const defaultProps = type.defaultProps; - for (propName in defaultProps) { - if (props[propName] === undefined) { - props[propName] = defaultProps[propName]; - } - } - } - - return ReactElement( - type, - key, - ref, - undefined, - undefined, - ReactCurrentOwner.current, - props, - ); -} - -/** - * https://github.com/reactjs/rfcs/pull/107 - * @param {*} type - * @param {object} props - * @param {string} key - */ -export function jsxDEV(type, config, maybeKey, source, self) { - let propName; - - // Reserved names are extracted - const props = {}; - - 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.
- // or
). We want to deprecate key spread, - // but as an intermediary step, we will use jsxDEV for everything except - //
, because we aren't currently able to tell if - // key is explicitly declared to be undefined or not. - if (maybeKey !== undefined) { - if (__DEV__) { - checkKeyStringCoercion(maybeKey); - } - key = '' + maybeKey; - } - - if (hasValidKey(config)) { - if (__DEV__) { - checkKeyStringCoercion(config.key); - } - key = '' + config.key; - } - - if (hasValidRef(config)) { - ref = config.ref; - warnIfStringRefCannotBeAutoConverted(config); - } - - // Remaining properties are added to a new props object - for (propName in config) { - if ( - hasOwnProperty.call(config, propName) && - !RESERVED_PROPS.hasOwnProperty(propName) - ) { - props[propName] = config[propName]; - } - } - - // Resolve default props - if (type && type.defaultProps) { - const defaultProps = type.defaultProps; - for (propName in defaultProps) { - if (props[propName] === undefined) { - props[propName] = defaultProps[propName]; - } - } - } - - if (key || ref) { - const displayName = - typeof type === 'function' - ? type.displayName || type.name || 'Unknown' - : type; - if (key) { - defineKeyPropWarningGetter(props, displayName); - } - if (ref) { - defineRefPropWarningGetter(props, displayName); - } - } - - return ReactElement( - type, - key, - ref, - self, - source, - ReactCurrentOwner.current, - props, - ); -} - -/** - * 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) { - let propName; - - // Reserved names are extracted - const props = {}; - - let key = null; - let ref = null; - let self = null; - let source = null; - - if (config != null) { - if (hasValidRef(config)) { - ref = config.ref; - - if (__DEV__) { - warnIfStringRefCannotBeAutoConverted(config); - } - } - if (hasValidKey(config)) { - if (__DEV__) { - checkKeyStringCoercion(config.key); - } - key = '' + config.key; - } - - self = config.__self === undefined ? null : config.__self; - source = config.__source === undefined ? null : config.__source; - // Remaining properties are added to a new props object - for (propName in config) { - if ( - hasOwnProperty.call(config, propName) && - !RESERVED_PROPS.hasOwnProperty(propName) - ) { - 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 = Array(childrenLength); - for (let i = 0; i < childrenLength; i++) { - childArray[i] = arguments[i + 2]; - } - if (__DEV__) { - if (Object.freeze) { - Object.freeze(childArray); - } - } - 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 || ref) { - const displayName = - typeof type === 'function' - ? type.displayName || type.name || 'Unknown' - : type; - if (key) { - defineKeyPropWarningGetter(props, displayName); - } - if (ref) { - defineRefPropWarningGetter(props, displayName); - } - } - } - return ReactElement( - type, - key, - ref, - self, - source, - ReactCurrentOwner.current, - props, - ); -} - -/** - * Return a function that produces ReactElements of a given type. - * See https://reactjs.org/docs/react-api.html#createfactory - */ -export function createFactory(type) { - const factory = createElement.bind(null, type); - // Expose the type on the factory and the prototype so that it can be - // easily accessed on elements. E.g. `.type === Foo`. - // This should not be named `constructor` since this may not be the function - // that created the element, and it may not even be a constructor. - // Legacy hook: remove it - factory.type = type; - return factory; -} - -export function cloneAndReplaceKey(oldElement, newKey) { - const newElement = ReactElement( - oldElement.type, - newKey, - oldElement.ref, - oldElement._self, - oldElement._source, - oldElement._owner, - oldElement.props, - ); - - return newElement; -} - -/** - * 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( - `React.cloneElement(...): 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 = element.ref; - // Self is preserved since the owner is preserved. - const self = element._self; - // Source is preserved since cloneElement is unlikely to be targeted by a - // transpiler, and the original source is probably a better indicator of the - // true owner. - const source = element._source; - - // Owner will be preserved, unless ref is overridden - let owner = element._owner; - - if (config != null) { - if (hasValidRef(config)) { - // Silently steal the ref from the parent. - ref = config.ref; - owner = ReactCurrentOwner.current; - } - if (hasValidKey(config)) { - if (__DEV__) { - checkKeyStringCoercion(config.key); - } - key = '' + config.key; - } - - // Remaining properties override existing props - let defaultProps; - if (element.type && element.type.defaultProps) { - defaultProps = element.type.defaultProps; - } - for (propName in config) { - if ( - hasOwnProperty.call(config, propName) && - !RESERVED_PROPS.hasOwnProperty(propName) - ) { - if (config[propName] === undefined && defaultProps !== undefined) { - // Resolve default props - props[propName] = defaultProps[propName]; - } 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 = Array(childrenLength); - for (let i = 0; i < childrenLength; i++) { - childArray[i] = arguments[i + 2]; - } - props.children = childArray; - } - - return ReactElement(element.type, key, ref, self, source, owner, props); -} - -/** - * 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 - ); -} + * @flow + */ + +import { + createElement as createElementProd, + createFactory as createFactoryProd, + cloneElement as cloneElementProd, +} from './ReactElementProd'; + +import { + createElementWithValidation, + createFactoryWithValidation, + cloneElementWithValidation, +} from './ReactElementValidator'; + +export {isValidElement, cloneAndReplaceKey} from './ReactElementProd'; + +export const createElement: any = __DEV__ + ? createElementWithValidation + : createElementProd; +export const cloneElement: any = __DEV__ + ? cloneElementWithValidation + : cloneElementProd; +export const createFactory: any = __DEV__ + ? createFactoryWithValidation + : createFactoryProd; diff --git a/packages/react/src/ReactElementProd.js b/packages/react/src/ReactElementProd.js new file mode 100644 index 0000000000000..e9d721b92693a --- /dev/null +++ b/packages/react/src/ReactElementProd.js @@ -0,0 +1,573 @@ +/** + * 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 {REACT_ELEMENT_TYPE} from 'shared/ReactSymbols'; +import assign from 'shared/assign'; +import hasOwnProperty from 'shared/hasOwnProperty'; +import {checkKeyStringCoercion} from 'shared/CheckStringCoercion'; + +import ReactCurrentOwner from './ReactCurrentOwner'; + +const RESERVED_PROPS = { + key: true, + ref: true, + __self: true, + __source: true, +}; + +let specialPropKeyWarningShown, + specialPropRefWarningShown, + didWarnAboutStringRefs; + +if (__DEV__) { + didWarnAboutStringRefs = {}; +} + +function hasValidRef(config) { + if (__DEV__) { + if (hasOwnProperty.call(config, 'ref')) { + const getter = Object.getOwnPropertyDescriptor(config, 'ref').get; + if (getter && getter.isReactWarning) { + return false; + } + } + } + return config.ref !== undefined; +} + +function hasValidKey(config) { + if (__DEV__) { + if (hasOwnProperty.call(config, 'key')) { + const getter = Object.getOwnPropertyDescriptor(config, 'key').get; + if (getter && getter.isReactWarning) { + return false; + } + } + } + return config.key !== undefined; +} + +function defineKeyPropWarningGetter(props, displayName) { + const warnAboutAccessingKey = function () { + if (__DEV__) { + if (!specialPropKeyWarningShown) { + specialPropKeyWarningShown = true; + console.error( + '%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://reactjs.org/link/special-props)', + displayName, + ); + } + } + }; + warnAboutAccessingKey.isReactWarning = true; + Object.defineProperty(props, 'key', { + get: warnAboutAccessingKey, + configurable: true, + }); +} + +function defineRefPropWarningGetter(props, displayName) { + const warnAboutAccessingRef = function () { + if (__DEV__) { + if (!specialPropRefWarningShown) { + specialPropRefWarningShown = true; + console.error( + '%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://reactjs.org/link/special-props)', + displayName, + ); + } + } + }; + warnAboutAccessingRef.isReactWarning = true; + Object.defineProperty(props, 'ref', { + get: warnAboutAccessingRef, + configurable: true, + }); +} + +function warnIfStringRefCannotBeAutoConverted(config) { + if (__DEV__) { + if ( + typeof config.ref === 'string' && + ReactCurrentOwner.current && + config.__self && + ReactCurrentOwner.current.stateNode !== config.__self + ) { + const componentName = getComponentNameFromType( + ReactCurrentOwner.current.type, + ); + + 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://reactjs.org/link/strict-mode-string-ref', + componentName, + config.ref, + ); + didWarnAboutStringRefs[componentName] = true; + } + } + } +} + +/** + * 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.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) { + const 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: type, + key: key, + ref: ref, + props: 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, + }); + // self and source are DEV only properties. + Object.defineProperty(element, '_self', { + configurable: false, + enumerable: false, + writable: false, + value: self, + }); + // Two elements created in two different places should be considered + // equal for testing purposes and therefore we hide it from enumeration. + Object.defineProperty(element, '_source', { + configurable: false, + enumerable: false, + writable: false, + value: source, + }); + if (Object.freeze) { + Object.freeze(element.props); + Object.freeze(element); + } + } + + return element; +} + +/** + * https://github.com/reactjs/rfcs/pull/107 + * @param {*} type + * @param {object} props + * @param {string} key + */ +export function jsx(type, config, maybeKey) { + let propName; + + // Reserved names are extracted + const props = {}; + + 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.
+ // or
). We want to deprecate key spread, + // but as an intermediary step, we will use jsxDEV for everything except + //
, because we aren't currently able to tell if + // key is explicitly declared to be undefined or not. + if (maybeKey !== undefined) { + if (__DEV__) { + checkKeyStringCoercion(maybeKey); + } + key = '' + maybeKey; + } + + if (hasValidKey(config)) { + if (__DEV__) { + checkKeyStringCoercion(config.key); + } + key = '' + config.key; + } + + if (hasValidRef(config)) { + ref = config.ref; + } + + // Remaining properties are added to a new props object + for (propName in config) { + if ( + hasOwnProperty.call(config, propName) && + !RESERVED_PROPS.hasOwnProperty(propName) + ) { + props[propName] = config[propName]; + } + } + + // Resolve default props + if (type && type.defaultProps) { + const defaultProps = type.defaultProps; + for (propName in defaultProps) { + if (props[propName] === undefined) { + props[propName] = defaultProps[propName]; + } + } + } + + return ReactElement( + type, + key, + ref, + undefined, + undefined, + ReactCurrentOwner.current, + props, + ); +} + +/** + * https://github.com/reactjs/rfcs/pull/107 + * @param {*} type + * @param {object} props + * @param {string} key + */ +export function jsxDEV(type, config, maybeKey, source, self) { + let propName; + + // Reserved names are extracted + const props = {}; + + 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.
+ // or
). We want to deprecate key spread, + // but as an intermediary step, we will use jsxDEV for everything except + //
, because we aren't currently able to tell if + // key is explicitly declared to be undefined or not. + if (maybeKey !== undefined) { + if (__DEV__) { + checkKeyStringCoercion(maybeKey); + } + key = '' + maybeKey; + } + + if (hasValidKey(config)) { + if (__DEV__) { + checkKeyStringCoercion(config.key); + } + key = '' + config.key; + } + + if (hasValidRef(config)) { + ref = config.ref; + warnIfStringRefCannotBeAutoConverted(config); + } + + // Remaining properties are added to a new props object + for (propName in config) { + if ( + hasOwnProperty.call(config, propName) && + !RESERVED_PROPS.hasOwnProperty(propName) + ) { + props[propName] = config[propName]; + } + } + + // Resolve default props + if (type && type.defaultProps) { + const defaultProps = type.defaultProps; + for (propName in defaultProps) { + if (props[propName] === undefined) { + props[propName] = defaultProps[propName]; + } + } + } + + if (key || ref) { + const displayName = + typeof type === 'function' + ? type.displayName || type.name || 'Unknown' + : type; + if (key) { + defineKeyPropWarningGetter(props, displayName); + } + if (ref) { + defineRefPropWarningGetter(props, displayName); + } + } + + return ReactElement( + type, + key, + ref, + self, + source, + ReactCurrentOwner.current, + props, + ); +} + +/** + * 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) { + let propName; + + // Reserved names are extracted + const props = {}; + + let key = null; + let ref = null; + let self = null; + let source = null; + + if (config != null) { + if (hasValidRef(config)) { + ref = config.ref; + + if (__DEV__) { + warnIfStringRefCannotBeAutoConverted(config); + } + } + if (hasValidKey(config)) { + if (__DEV__) { + checkKeyStringCoercion(config.key); + } + key = '' + config.key; + } + + self = config.__self === undefined ? null : config.__self; + source = config.__source === undefined ? null : config.__source; + // Remaining properties are added to a new props object + for (propName in config) { + if ( + hasOwnProperty.call(config, propName) && + !RESERVED_PROPS.hasOwnProperty(propName) + ) { + 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 = Array(childrenLength); + for (let i = 0; i < childrenLength; i++) { + childArray[i] = arguments[i + 2]; + } + if (__DEV__) { + if (Object.freeze) { + Object.freeze(childArray); + } + } + 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 || ref) { + const displayName = + typeof type === 'function' + ? type.displayName || type.name || 'Unknown' + : type; + if (key) { + defineKeyPropWarningGetter(props, displayName); + } + if (ref) { + defineRefPropWarningGetter(props, displayName); + } + } + } + return ReactElement( + type, + key, + ref, + self, + source, + ReactCurrentOwner.current, + props, + ); +} + +/** + * Return a function that produces ReactElements of a given type. + * See https://reactjs.org/docs/react-api.html#createfactory + */ +export function createFactory(type) { + const factory = createElement.bind(null, type); + // Expose the type on the factory and the prototype so that it can be + // easily accessed on elements. E.g. `.type === Foo`. + // This should not be named `constructor` since this may not be the function + // that created the element, and it may not even be a constructor. + // Legacy hook: remove it + factory.type = type; + return factory; +} + +export function cloneAndReplaceKey(oldElement, newKey) { + const newElement = ReactElement( + oldElement.type, + newKey, + oldElement.ref, + oldElement._self, + oldElement._source, + oldElement._owner, + oldElement.props, + ); + + return newElement; +} + +/** + * 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( + `React.cloneElement(...): 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 = element.ref; + // Self is preserved since the owner is preserved. + const self = element._self; + // Source is preserved since cloneElement is unlikely to be targeted by a + // transpiler, and the original source is probably a better indicator of the + // true owner. + const source = element._source; + + // Owner will be preserved, unless ref is overridden + let owner = element._owner; + + if (config != null) { + if (hasValidRef(config)) { + // Silently steal the ref from the parent. + ref = config.ref; + owner = ReactCurrentOwner.current; + } + if (hasValidKey(config)) { + if (__DEV__) { + checkKeyStringCoercion(config.key); + } + key = '' + config.key; + } + + // Remaining properties override existing props + let defaultProps; + if (element.type && element.type.defaultProps) { + defaultProps = element.type.defaultProps; + } + for (propName in config) { + if ( + hasOwnProperty.call(config, propName) && + !RESERVED_PROPS.hasOwnProperty(propName) + ) { + if (config[propName] === undefined && defaultProps !== undefined) { + // Resolve default props + props[propName] = defaultProps[propName]; + } 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 = Array(childrenLength); + for (let i = 0; i < childrenLength; i++) { + childArray[i] = arguments[i + 2]; + } + props.children = childArray; + } + + return ReactElement(element.type, key, ref, self, source, owner, props); +} + +/** + * 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 + ); +} diff --git a/packages/react/src/ReactElementValidator.js b/packages/react/src/ReactElementValidator.js index 44e9033fd3578..78f8e498d816f 100644 --- a/packages/react/src/ReactElementValidator.js +++ b/packages/react/src/ReactElementValidator.js @@ -30,7 +30,7 @@ import { createElement, cloneElement, jsxDEV, -} from './ReactElement'; +} from './ReactElementProd'; import {setExtraStackFrame} from './ReactDebugCurrentFrame'; import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame'; import hasOwnProperty from 'shared/hasOwnProperty'; diff --git a/packages/react/src/ReactServer.experimental.js b/packages/react/src/ReactServer.experimental.js index 45baaadc89f7b..330707b7b5992 100644 --- a/packages/react/src/ReactServer.experimental.js +++ b/packages/react/src/ReactServer.experimental.js @@ -14,6 +14,43 @@ export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './R export {default as __SECRET_SERVER_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './ReactServerSharedInternals'; +import {forEach, map, count, toArray, only} from './ReactChildren'; +import { + REACT_FRAGMENT_TYPE, + REACT_PROFILER_TYPE, + REACT_STRICT_MODE_TYPE, + REACT_SUSPENSE_TYPE, + REACT_DEBUG_TRACING_MODE_TYPE, +} from 'shared/ReactSymbols'; +import {cloneElement, createElement, isValidElement} from './ReactElement'; +import {createRef} from './ReactCreateRef'; +import {createServerContext} from './ReactServerContext'; +import { + use, + useId, + useCallback, + useContext, + useDebugValue, + useMemo, + getCacheSignal, + getCacheForType, +} from './ReactHooks'; +import {forwardRef} from './ReactForwardRef'; +import {lazy} from './ReactLazy'; +import {memo} from './ReactMemo'; +import {cache} from './ReactCache'; +import {startTransition} from './ReactStartTransition'; +import {postpone} from './ReactPostpone'; +import version from 'shared/ReactVersion'; + +const Children = { + map, + forEach, + count, + toArray, + only, +}; + // These are server-only export { taintUniqueValue as experimental_taintUniqueValue, @@ -22,10 +59,10 @@ export { export { Children, - Fragment, - Profiler, - StrictMode, - Suspense, + REACT_FRAGMENT_TYPE as Fragment, + REACT_PROFILER_TYPE as Profiler, + REACT_STRICT_MODE_TYPE as StrictMode, + REACT_SUSPENSE_TYPE as Suspense, cloneElement, createElement, createRef, @@ -37,15 +74,15 @@ export { memo, cache, startTransition, - unstable_DebugTracingMode, - unstable_SuspenseList, - unstable_getCacheSignal, - unstable_getCacheForType, - unstable_postpone, + REACT_DEBUG_TRACING_MODE_TYPE as unstable_DebugTracingMode, + REACT_SUSPENSE_TYPE as unstable_SuspenseList, + getCacheSignal as unstable_getCacheSignal, + getCacheForType as unstable_getCacheForType, + postpone as unstable_postpone, useId, useCallback, useContext, useDebugValue, useMemo, version, -} from './React'; +}; diff --git a/packages/react/src/ReactServer.js b/packages/react/src/ReactServer.js index 1bc2b3036f455..5631319132fda 100644 --- a/packages/react/src/ReactServer.js +++ b/packages/react/src/ReactServer.js @@ -14,12 +14,45 @@ export {default as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './R export {default as __SECRET_SERVER_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED} from './ReactServerSharedInternals'; +import {forEach, map, count, toArray, only} from './ReactChildren'; +import { + REACT_FRAGMENT_TYPE, + REACT_PROFILER_TYPE, + REACT_STRICT_MODE_TYPE, + REACT_SUSPENSE_TYPE, +} from 'shared/ReactSymbols'; +import {cloneElement, createElement, isValidElement} from './ReactElement'; +import {createRef} from './ReactCreateRef'; +import {createServerContext} from './ReactServerContext'; +import { + use, + useId, + useCallback, + useContext, + useDebugValue, + useMemo, +} from './ReactHooks'; +import {forwardRef} from './ReactForwardRef'; +import {lazy} from './ReactLazy'; +import {memo} from './ReactMemo'; +import {cache} from './ReactCache'; +import {startTransition} from './ReactStartTransition'; +import version from 'shared/ReactVersion'; + +const Children = { + map, + forEach, + count, + toArray, + only, +}; + export { Children, - Fragment, - Profiler, - StrictMode, - Suspense, + REACT_FRAGMENT_TYPE as Fragment, + REACT_PROFILER_TYPE as Profiler, + REACT_STRICT_MODE_TYPE as StrictMode, + REACT_SUSPENSE_TYPE as Suspense, cloneElement, createElement, createRef, @@ -37,4 +70,4 @@ export { useDebugValue, useMemo, version, -} from './React'; +};