Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"homepage": "https://reactjs.org/",
"dependencies": {
"object-assign": "^4.1.1",
"prop-types": "^15.7.2",
"react-is": "^16.12.0"
},
"devDependencies": {
Expand Down
147 changes: 78 additions & 69 deletions src/ReactShallowRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import {isForwardRef, isMemo, ForwardRef} from 'react-is';
import describeComponentFrame from './shared/describeComponentFrame';
import getComponentName from './shared/getComponentName';
import shallowEqual from './shared/shallowEqual';
import checkPropTypes from 'prop-types/checkPropTypes';
import checkPropTypes from './shared/checkPropTypes';
import ReactSharedInternals from './shared/ReactSharedInternals';
import {error} from './shared/consoleWithStackDev';
import is from './shared/objectIs';

const {ReactCurrentDispatcher} = ReactSharedInternals;
const {ReactCurrentDispatcher, ReactDebugCurrentFrame} = ReactSharedInternals;

const RE_RENDER_LIMIT = 25;

Expand Down Expand Up @@ -503,91 +503,100 @@ See https://fb.me/react-invalid-hook-call for tips about how to debug and fix th
this._context = getMaskedContext(elementType.contextTypes, context);

// Inner memo component props aren't currently validated in createElement.
if (isMemo(element) && elementType.propTypes) {
currentlyValidatingElement = element;
checkPropTypes(
elementType.propTypes,
element.props,
'prop',
getComponentName(elementType),
getStackAddendum,
);
let prevGetStack;
if (process.env.NODE_ENV !== 'production') {
prevGetStack = ReactDebugCurrentFrame.getCurrentStack;
ReactDebugCurrentFrame.getCurrentStack = getStackAddendum;
}

if (this._instance) {
this._updateClassComponent(elementType, element, this._context);
} else {
if (shouldConstruct(elementType)) {
this._instance = new elementType(
try {
if (isMemo(element) && elementType.propTypes) {
currentlyValidatingElement = element;
checkPropTypes(
elementType.propTypes,
element.props,
this._context,
this._updater,
'prop',
getComponentName(elementType),
);
if (typeof elementType.getDerivedStateFromProps === 'function') {
const partialState = elementType.getDerivedStateFromProps.call(
null,
}

if (this._instance) {
this._updateClassComponent(elementType, element, this._context);
} else {
if (shouldConstruct(elementType)) {
this._instance = new elementType(
element.props,
this._instance.state,
this._context,
this._updater,
);
if (partialState != null) {
this._instance.state = Object.assign(
{},
if (typeof elementType.getDerivedStateFromProps === 'function') {
const partialState = elementType.getDerivedStateFromProps.call(
null,
element.props,
this._instance.state,
partialState,
);
if (partialState != null) {
this._instance.state = Object.assign(
{},
this._instance.state,
partialState,
);
}
}
}

if (elementType.contextTypes) {
currentlyValidatingElement = element;
checkPropTypes(
elementType.contextTypes,
this._context,
'context',
getName(elementType, this._instance),
getStackAddendum,
);
if (elementType.contextTypes) {
currentlyValidatingElement = element;
checkPropTypes(
elementType.contextTypes,
this._context,
'context',
getName(elementType, this._instance),
);

currentlyValidatingElement = null;
}
currentlyValidatingElement = null;
}

this._mountClassComponent(elementType, element, this._context);
} else {
let shouldRender = true;
if (isMemo(element) && previousElement !== null) {
// This is a Memo component that is being re-rendered.
const compare = element.type.compare || shallowEqual;
if (compare(previousElement.props, element.props)) {
shouldRender = false;
this._mountClassComponent(elementType, element, this._context);
} else {
let shouldRender = true;
if (isMemo(element) && previousElement !== null) {
// This is a Memo component that is being re-rendered.
const compare = element.type.compare || shallowEqual;
if (compare(previousElement.props, element.props)) {
shouldRender = false;
}
}
}
if (shouldRender) {
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = this._dispatcher;
try {
// elementType could still be a ForwardRef if it was
// nested inside Memo.
if (elementType.$$typeof === ForwardRef) {
if (!(typeof elementType.render === 'function')) {
throw Error(
`forwardRef requires a render function but was given ${typeof elementType.render}.`,
if (shouldRender) {
const prevDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = this._dispatcher;
try {
// elementType could still be a ForwardRef if it was
// nested inside Memo.
if (elementType.$$typeof === ForwardRef) {
if (!(typeof elementType.render === 'function')) {
throw Error(
`forwardRef requires a render function but was given ${typeof elementType.render}.`,
);
}
this._rendered = elementType.render.call(
undefined,
element.props,
element.ref,
);
} else {
this._rendered = elementType(element.props, this._context);
}
this._rendered = elementType.render.call(
undefined,
element.props,
element.ref,
);
} else {
this._rendered = elementType(element.props, this._context);
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
}
} finally {
ReactCurrentDispatcher.current = prevDispatcher;
this._finishHooks(element, context);
}
this._finishHooks(element, context);
}
}
}
} finally {
if (process.env.NODE_ENV !== 'production') {
ReactDebugCurrentFrame.getCurrentStack = prevGetStack;
}
}

this._rendering = false;
this._updater._invokeCallbacks();
Expand Down
80 changes: 80 additions & 0 deletions src/shared/checkPropTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import {error as consoleError} from './consoleWithStackDev';

let loggedTypeFailures = {};

export default function checkPropTypes(
typeSpecs,
values,
location,
componentName,
) {
if (process.env.NODE_ENV !== 'production') {
let has = Function.call.bind(Object.prototype.hasOwnProperty);
for (let typeSpecName in typeSpecs) {
if (has(typeSpecs, typeSpecName)) {
let error;
// Prop type validation may throw. In case they do, we don't want to
// fail the render phase where it didn't fail before. So we log it.
// After these have been cleaned up, we'll let them throw.
try {
// This is intentionally an invariant that gets caught. It's the same
// behavior as without this statement except with a better message.
if (typeof typeSpecs[typeSpecName] !== 'function') {
let err = Error(
(componentName || 'React class') +
': ' +
location +
' type `' +
typeSpecName +
'` is invalid; ' +
'it must be a function, usually from the `prop-types` package, but received `' +
typeof typeSpecs[typeSpecName] +
'`.' +
'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.',
);
err.name = 'Invariant Violation';
throw err;
}
error = typeSpecs[typeSpecName](
values,
typeSpecName,
componentName,
location,
null,
'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED',
);
} catch (ex) {
error = ex;
}
if (error && !(error instanceof Error)) {
consoleError(
'%s: type specification of %s' +
' `%s` is invalid; the type checker ' +
'function must return `null` or an `Error` but returned a %s. ' +
'You may have forgotten to pass an argument to the type checker ' +
'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
'shape all require an argument).',
componentName || 'React class',
location,
typeSpecName,
typeof error,
);
}
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
// Only monitor this failure once because there tends to be a lot of the
// same error.
loggedTypeFailures[error.message] = true;
consoleError('Failed %s type: %s', location, error.message);
}
}
}
}
}
2 changes: 1 addition & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3553,7 +3553,7 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.3"

prop-types@^15.6.2, prop-types@^15.7.2:
prop-types@^15.6.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
Expand Down