mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Unify traversal logic in createElement
This moves all type traversal into createElement. When lazy resolves, we call createElement once to re-check.
This commit is contained in:
+11
-133
@@ -13,6 +13,7 @@ import type {FiberRoot} from './ReactFiberRoot';
|
||||
import type {ExpirationTime} from './ReactFiberExpirationTime';
|
||||
import type {SuspenseState} from './ReactFiberSuspenseComponent';
|
||||
|
||||
import React from 'react';
|
||||
import checkPropTypes from 'prop-types/checkPropTypes';
|
||||
|
||||
import {
|
||||
@@ -50,11 +51,11 @@ import {
|
||||
debugRenderPhaseSideEffectsForStrictMode,
|
||||
enableProfilerTimer,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import isValidElementType from 'shared/isValidElementType';
|
||||
import invariant from 'shared/invariant';
|
||||
import shallowEqual from 'shared/shallowEqual';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import ReactStrictModeWarnings from './ReactStrictModeWarnings';
|
||||
import {refineResolvedLazyComponent} from 'shared/ReactLazyComponent';
|
||||
import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
|
||||
import warning from 'shared/warning';
|
||||
import warningWithoutStack from 'shared/warningWithoutStack';
|
||||
@@ -214,23 +215,6 @@ function updateForwardRef(
|
||||
nextProps: any,
|
||||
renderExpirationTime: ExpirationTime,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
// Lazy component props can't be validated in createElement
|
||||
// because they're only guaranteed to be resolved here.
|
||||
const innerPropTypes = Component.propTypes;
|
||||
if (innerPropTypes) {
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentName(Component),
|
||||
getCurrentFiberStackInDev,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const render = Component.render;
|
||||
const ref = workInProgress.ref;
|
||||
|
||||
@@ -292,20 +276,6 @@ function updateMemoComponent(
|
||||
renderExpirationTime,
|
||||
);
|
||||
}
|
||||
if (__DEV__) {
|
||||
const innerPropTypes = type.propTypes;
|
||||
if (innerPropTypes) {
|
||||
// Inner memo component props aren't currently validated in createElement.
|
||||
// We could move it there, but we'd still need this for lazy code path.
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentName(type),
|
||||
getCurrentFiberStackInDev,
|
||||
);
|
||||
}
|
||||
}
|
||||
let child = createFiberFromTypeAndProps(
|
||||
Component.type,
|
||||
null,
|
||||
@@ -319,21 +289,6 @@ function updateMemoComponent(
|
||||
workInProgress.child = child;
|
||||
return child;
|
||||
}
|
||||
if (__DEV__) {
|
||||
const type = Component.type;
|
||||
const innerPropTypes = type.propTypes;
|
||||
if (innerPropTypes) {
|
||||
// Inner memo component props aren't currently validated in createElement.
|
||||
// We could move it there, but we'd still need this for lazy code path.
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentName(type),
|
||||
getCurrentFiberStackInDev,
|
||||
);
|
||||
}
|
||||
}
|
||||
let currentChild = ((current.child: any): Fiber); // This is always exactly one child
|
||||
if (updateExpirationTime < renderExpirationTime) {
|
||||
// This will be the props with resolved defaultProps,
|
||||
@@ -371,30 +326,6 @@ function updateSimpleMemoComponent(
|
||||
updateExpirationTime,
|
||||
renderExpirationTime: ExpirationTime,
|
||||
): null | Fiber {
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
// Lazy component props can't be validated in createElement
|
||||
// because they're only guaranteed to be resolved here.
|
||||
let outerMemoType = workInProgress.elementType;
|
||||
if (outerMemoType.$$typeof === REACT_LAZY_TYPE) {
|
||||
// We warn when you define propTypes on lazy()
|
||||
// so let's just skip over it to find memo() outer wrapper.
|
||||
// Inner props for memo are validated later.
|
||||
outerMemoType = refineResolvedLazyComponent(outerMemoType);
|
||||
}
|
||||
const outerPropTypes = outerMemoType && (outerMemoType: any).propTypes;
|
||||
if (outerPropTypes) {
|
||||
checkPropTypes(
|
||||
outerPropTypes,
|
||||
nextProps, // Resolved (SimpleMemoComponent has no defaultProps)
|
||||
'prop',
|
||||
getComponentName(outerMemoType),
|
||||
getCurrentFiberStackInDev,
|
||||
);
|
||||
}
|
||||
// Inner propTypes will be validated in the function component path.
|
||||
}
|
||||
}
|
||||
if (current !== null && updateExpirationTime < renderExpirationTime) {
|
||||
const prevProps = current.memoizedProps;
|
||||
if (
|
||||
@@ -484,23 +415,6 @@ function updateFunctionComponent(
|
||||
nextProps: any,
|
||||
renderExpirationTime,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
// Lazy component props can't be validated in createElement
|
||||
// because they're only guaranteed to be resolved here.
|
||||
const innerPropTypes = Component.propTypes;
|
||||
if (innerPropTypes) {
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentName(Component),
|
||||
getCurrentFiberStackInDev,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
|
||||
const context = getMaskedContext(workInProgress, unmaskedContext);
|
||||
|
||||
@@ -535,23 +449,6 @@ function updateClassComponent(
|
||||
nextProps,
|
||||
renderExpirationTime: ExpirationTime,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
// Lazy component props can't be validated in createElement
|
||||
// because they're only guaranteed to be resolved here.
|
||||
const innerPropTypes = Component.propTypes;
|
||||
if (innerPropTypes) {
|
||||
checkPropTypes(
|
||||
innerPropTypes,
|
||||
nextProps, // Resolved props
|
||||
'prop',
|
||||
getComponentName(Component),
|
||||
getCurrentFiberStackInDev,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push context providers early to prevent context stack mismatches.
|
||||
// During mounting we don't know the child context yet as the instance doesn't exist.
|
||||
// We will invalidate the child context in finishClassComponent() right after rendering.
|
||||
@@ -901,6 +798,15 @@ function mountLazyComponent(
|
||||
startWorkTimer(workInProgress);
|
||||
const resolvedProps = resolveDefaultProps(Component, props);
|
||||
let child;
|
||||
|
||||
if (__DEV__) {
|
||||
// Trigger propTypes validation for the resolved type now that
|
||||
// createElement() can look deeper and find propTypes and defaultProps.
|
||||
if (isValidElementType(Component)) {
|
||||
React.createElement(Component, resolvedProps);
|
||||
}
|
||||
}
|
||||
|
||||
switch (resolvedTag) {
|
||||
case FunctionComponent: {
|
||||
child = updateFunctionComponent(
|
||||
@@ -933,20 +839,6 @@ function mountLazyComponent(
|
||||
break;
|
||||
}
|
||||
case MemoComponent: {
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
const outerPropTypes = Component.propTypes;
|
||||
if (outerPropTypes) {
|
||||
checkPropTypes(
|
||||
outerPropTypes,
|
||||
resolvedProps, // Resolved for outer only
|
||||
'prop',
|
||||
getComponentName(Component),
|
||||
getCurrentFiberStackInDev,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
child = updateMemoComponent(
|
||||
null,
|
||||
workInProgress,
|
||||
@@ -1877,20 +1769,6 @@ function beginWork(
|
||||
const unresolvedProps = workInProgress.pendingProps;
|
||||
// Resolve outer props first, then resolve inner props.
|
||||
let resolvedProps = resolveDefaultProps(type, unresolvedProps);
|
||||
if (__DEV__) {
|
||||
if (workInProgress.type !== workInProgress.elementType) {
|
||||
const outerPropTypes = type.propTypes;
|
||||
if (outerPropTypes) {
|
||||
checkPropTypes(
|
||||
outerPropTypes,
|
||||
resolvedProps, // Resolved for outer only
|
||||
'prop',
|
||||
getComponentName(type),
|
||||
getCurrentFiberStackInDev,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
resolvedProps = resolveDefaultProps(type.type, resolvedProps);
|
||||
return updateMemoComponent(
|
||||
current,
|
||||
|
||||
@@ -15,10 +15,12 @@
|
||||
import lowPriorityWarning from 'shared/lowPriorityWarning';
|
||||
import isValidElementType from 'shared/isValidElementType';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
import {refineResolvedLazyComponent} from 'shared/ReactLazyComponent';
|
||||
import {
|
||||
getIteratorFn,
|
||||
REACT_FORWARD_REF_TYPE,
|
||||
REACT_MEMO_TYPE,
|
||||
REACT_LAZY_TYPE,
|
||||
REACT_FRAGMENT_TYPE,
|
||||
REACT_ELEMENT_TYPE,
|
||||
} from 'shared/ReactSymbols';
|
||||
@@ -178,6 +180,61 @@ function validateChildKeys(node, parentType) {
|
||||
}
|
||||
}
|
||||
|
||||
function validatePropTypesRecursively(type, props) {
|
||||
if (type === null || type === undefined || typeof type === 'string') {
|
||||
return;
|
||||
}
|
||||
let propTypes;
|
||||
let innerType;
|
||||
if (typeof type === 'function') {
|
||||
propTypes = type.propTypes;
|
||||
} else if (typeof type === 'object') {
|
||||
switch (type.$$typeof) {
|
||||
case REACT_FORWARD_REF_TYPE: {
|
||||
propTypes = type.propTypes;
|
||||
break;
|
||||
}
|
||||
case REACT_LAZY_TYPE: {
|
||||
propTypes = type.propTypes;
|
||||
innerType = refineResolvedLazyComponent(type);
|
||||
break;
|
||||
}
|
||||
case REACT_MEMO_TYPE: {
|
||||
propTypes = type.propTypes;
|
||||
innerType = type.type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Common case.
|
||||
if (propTypes) {
|
||||
const name = getComponentName(type);
|
||||
checkPropTypes(
|
||||
propTypes,
|
||||
props,
|
||||
'prop',
|
||||
name,
|
||||
ReactDebugCurrentFrame.getStackAddendum,
|
||||
);
|
||||
}
|
||||
// A type may have an inner type (e.g. React.memo or React.lazy).
|
||||
// That's what the recursive case is for.
|
||||
if (innerType) {
|
||||
// Inner type may have its own defaultProps
|
||||
let innerProps = props;
|
||||
if (innerType && innerType.defaultProps) {
|
||||
innerProps = {...props};
|
||||
const innerDefaultProps = innerType.defaultProps;
|
||||
for (const propName in innerDefaultProps) {
|
||||
if (innerProps[propName] === undefined) {
|
||||
innerProps[propName] = innerDefaultProps[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
validatePropTypesRecursively(innerType, innerProps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an element, validate that its props follow the propTypes definition,
|
||||
* provided by the type.
|
||||
@@ -189,45 +246,27 @@ function validatePropTypes(element) {
|
||||
if (type === null || type === undefined || typeof type === 'string') {
|
||||
return;
|
||||
}
|
||||
const name = getComponentName(type);
|
||||
let propTypes;
|
||||
|
||||
setCurrentlyValidatingElement(element);
|
||||
validatePropTypesRecursively(type, element.props);
|
||||
setCurrentlyValidatingElement(null);
|
||||
|
||||
if (typeof type === 'function') {
|
||||
propTypes = type.propTypes;
|
||||
} else if (
|
||||
typeof type === 'object' &&
|
||||
(type.$$typeof === REACT_FORWARD_REF_TYPE ||
|
||||
// Note: Memo only checks outer props here.
|
||||
// Inner props are checked in the reconciler.
|
||||
type.$$typeof === REACT_MEMO_TYPE)
|
||||
) {
|
||||
propTypes = type.propTypes;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (propTypes) {
|
||||
setCurrentlyValidatingElement(element);
|
||||
checkPropTypes(
|
||||
propTypes,
|
||||
element.props,
|
||||
'prop',
|
||||
name,
|
||||
ReactDebugCurrentFrame.getStackAddendum,
|
||||
);
|
||||
setCurrentlyValidatingElement(null);
|
||||
} else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
|
||||
propTypesMisspellWarningShown = true;
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?',
|
||||
name || 'Unknown',
|
||||
);
|
||||
}
|
||||
if (typeof type.getDefaultProps === 'function') {
|
||||
warningWithoutStack(
|
||||
type.getDefaultProps.isReactClassApproved,
|
||||
'getDefaultProps is only used on classic React.createClass ' +
|
||||
'definitions. Use a static property named `defaultProps` instead.',
|
||||
);
|
||||
if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
|
||||
propTypesMisspellWarningShown = true;
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?',
|
||||
getComponentName(type) || 'Unknown',
|
||||
);
|
||||
}
|
||||
if (typeof type.getDefaultProps === 'function') {
|
||||
warningWithoutStack(
|
||||
type.getDefaultProps.isReactClassApproved,
|
||||
'getDefaultProps is only used on classic React.createClass ' +
|
||||
'definitions. Use a static property named `defaultProps` instead.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ const UMD_PROFILING = bundleTypes.UMD_PROFILING;
|
||||
const HAS_NO_SIDE_EFFECTS_ON_IMPORT = false;
|
||||
// const HAS_SIDE_EFFECTS_ON_IMPORT = true;
|
||||
const importSideEffects = Object.freeze({
|
||||
react: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
'prop-types/checkPropTypes': HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
deepFreezeAndThrowOnMutationInDev: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
scheduler: HAS_NO_SIDE_EFFECTS_ON_IMPORT,
|
||||
|
||||
Reference in New Issue
Block a user