Skip to content

Commit 2e59ba1

Browse files
authored
Inline propTypes checking code (#15)
1 parent 3889375 commit 2e59ba1

File tree

4 files changed

+159
-71
lines changed

4 files changed

+159
-71
lines changed

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
"homepage": "https://reactjs.org/",
1818
"dependencies": {
1919
"object-assign": "^4.1.1",
20-
"prop-types": "^15.7.2",
2120
"react-is": "^16.12.0"
2221
},
2322
"devDependencies": {

src/ReactShallowRenderer.js

Lines changed: 78 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ import {isForwardRef, isMemo, ForwardRef} from 'react-is';
1212
import describeComponentFrame from './shared/describeComponentFrame';
1313
import getComponentName from './shared/getComponentName';
1414
import shallowEqual from './shared/shallowEqual';
15-
import checkPropTypes from 'prop-types/checkPropTypes';
15+
import checkPropTypes from './shared/checkPropTypes';
1616
import ReactSharedInternals from './shared/ReactSharedInternals';
1717
import {error} from './shared/consoleWithStackDev';
1818
import is from './shared/objectIs';
1919

20-
const {ReactCurrentDispatcher} = ReactSharedInternals;
20+
const {ReactCurrentDispatcher, ReactDebugCurrentFrame} = ReactSharedInternals;
2121

2222
const RE_RENDER_LIMIT = 25;
2323

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

505505
// Inner memo component props aren't currently validated in createElement.
506-
if (isMemo(element) && elementType.propTypes) {
507-
currentlyValidatingElement = element;
508-
checkPropTypes(
509-
elementType.propTypes,
510-
element.props,
511-
'prop',
512-
getComponentName(elementType),
513-
getStackAddendum,
514-
);
506+
let prevGetStack;
507+
if (process.env.NODE_ENV !== 'production') {
508+
prevGetStack = ReactDebugCurrentFrame.getCurrentStack;
509+
ReactDebugCurrentFrame.getCurrentStack = getStackAddendum;
515510
}
516-
517-
if (this._instance) {
518-
this._updateClassComponent(elementType, element, this._context);
519-
} else {
520-
if (shouldConstruct(elementType)) {
521-
this._instance = new elementType(
511+
try {
512+
if (isMemo(element) && elementType.propTypes) {
513+
currentlyValidatingElement = element;
514+
checkPropTypes(
515+
elementType.propTypes,
522516
element.props,
523-
this._context,
524-
this._updater,
517+
'prop',
518+
getComponentName(elementType),
525519
);
526-
if (typeof elementType.getDerivedStateFromProps === 'function') {
527-
const partialState = elementType.getDerivedStateFromProps.call(
528-
null,
520+
}
521+
522+
if (this._instance) {
523+
this._updateClassComponent(elementType, element, this._context);
524+
} else {
525+
if (shouldConstruct(elementType)) {
526+
this._instance = new elementType(
529527
element.props,
530-
this._instance.state,
528+
this._context,
529+
this._updater,
531530
);
532-
if (partialState != null) {
533-
this._instance.state = Object.assign(
534-
{},
531+
if (typeof elementType.getDerivedStateFromProps === 'function') {
532+
const partialState = elementType.getDerivedStateFromProps.call(
533+
null,
534+
element.props,
535535
this._instance.state,
536-
partialState,
537536
);
537+
if (partialState != null) {
538+
this._instance.state = Object.assign(
539+
{},
540+
this._instance.state,
541+
partialState,
542+
);
543+
}
538544
}
539-
}
540545

541-
if (elementType.contextTypes) {
542-
currentlyValidatingElement = element;
543-
checkPropTypes(
544-
elementType.contextTypes,
545-
this._context,
546-
'context',
547-
getName(elementType, this._instance),
548-
getStackAddendum,
549-
);
546+
if (elementType.contextTypes) {
547+
currentlyValidatingElement = element;
548+
checkPropTypes(
549+
elementType.contextTypes,
550+
this._context,
551+
'context',
552+
getName(elementType, this._instance),
553+
);
550554

551-
currentlyValidatingElement = null;
552-
}
555+
currentlyValidatingElement = null;
556+
}
553557

554-
this._mountClassComponent(elementType, element, this._context);
555-
} else {
556-
let shouldRender = true;
557-
if (isMemo(element) && previousElement !== null) {
558-
// This is a Memo component that is being re-rendered.
559-
const compare = element.type.compare || shallowEqual;
560-
if (compare(previousElement.props, element.props)) {
561-
shouldRender = false;
558+
this._mountClassComponent(elementType, element, this._context);
559+
} else {
560+
let shouldRender = true;
561+
if (isMemo(element) && previousElement !== null) {
562+
// This is a Memo component that is being re-rendered.
563+
const compare = element.type.compare || shallowEqual;
564+
if (compare(previousElement.props, element.props)) {
565+
shouldRender = false;
566+
}
562567
}
563-
}
564-
if (shouldRender) {
565-
const prevDispatcher = ReactCurrentDispatcher.current;
566-
ReactCurrentDispatcher.current = this._dispatcher;
567-
try {
568-
// elementType could still be a ForwardRef if it was
569-
// nested inside Memo.
570-
if (elementType.$$typeof === ForwardRef) {
571-
if (!(typeof elementType.render === 'function')) {
572-
throw Error(
573-
`forwardRef requires a render function but was given ${typeof elementType.render}.`,
568+
if (shouldRender) {
569+
const prevDispatcher = ReactCurrentDispatcher.current;
570+
ReactCurrentDispatcher.current = this._dispatcher;
571+
try {
572+
// elementType could still be a ForwardRef if it was
573+
// nested inside Memo.
574+
if (elementType.$$typeof === ForwardRef) {
575+
if (!(typeof elementType.render === 'function')) {
576+
throw Error(
577+
`forwardRef requires a render function but was given ${typeof elementType.render}.`,
578+
);
579+
}
580+
this._rendered = elementType.render.call(
581+
undefined,
582+
element.props,
583+
element.ref,
574584
);
585+
} else {
586+
this._rendered = elementType(element.props, this._context);
575587
}
576-
this._rendered = elementType.render.call(
577-
undefined,
578-
element.props,
579-
element.ref,
580-
);
581-
} else {
582-
this._rendered = elementType(element.props, this._context);
588+
} finally {
589+
ReactCurrentDispatcher.current = prevDispatcher;
583590
}
584-
} finally {
585-
ReactCurrentDispatcher.current = prevDispatcher;
591+
this._finishHooks(element, context);
586592
}
587-
this._finishHooks(element, context);
588593
}
589594
}
590-
}
595+
} finally {
596+
if (process.env.NODE_ENV !== 'production') {
597+
ReactDebugCurrentFrame.getCurrentStack = prevGetStack;
598+
}
599+
}
591600

592601
this._rendering = false;
593602
this._updater._invokeCallbacks();

src/shared/checkPropTypes.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
import {error as consoleError} from './consoleWithStackDev';
10+
11+
let loggedTypeFailures = {};
12+
13+
export default function checkPropTypes(
14+
typeSpecs,
15+
values,
16+
location,
17+
componentName,
18+
) {
19+
if (process.env.NODE_ENV !== 'production') {
20+
let has = Function.call.bind(Object.prototype.hasOwnProperty);
21+
for (let typeSpecName in typeSpecs) {
22+
if (has(typeSpecs, typeSpecName)) {
23+
let error;
24+
// Prop type validation may throw. In case they do, we don't want to
25+
// fail the render phase where it didn't fail before. So we log it.
26+
// After these have been cleaned up, we'll let them throw.
27+
try {
28+
// This is intentionally an invariant that gets caught. It's the same
29+
// behavior as without this statement except with a better message.
30+
if (typeof typeSpecs[typeSpecName] !== 'function') {
31+
let err = Error(
32+
(componentName || 'React class') +
33+
': ' +
34+
location +
35+
' type `' +
36+
typeSpecName +
37+
'` is invalid; ' +
38+
'it must be a function, usually from the `prop-types` package, but received `' +
39+
typeof typeSpecs[typeSpecName] +
40+
'`.' +
41+
'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.',
42+
);
43+
err.name = 'Invariant Violation';
44+
throw err;
45+
}
46+
error = typeSpecs[typeSpecName](
47+
values,
48+
typeSpecName,
49+
componentName,
50+
location,
51+
null,
52+
'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED',
53+
);
54+
} catch (ex) {
55+
error = ex;
56+
}
57+
if (error && !(error instanceof Error)) {
58+
consoleError(
59+
'%s: type specification of %s' +
60+
' `%s` is invalid; the type checker ' +
61+
'function must return `null` or an `Error` but returned a %s. ' +
62+
'You may have forgotten to pass an argument to the type checker ' +
63+
'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
64+
'shape all require an argument).',
65+
componentName || 'React class',
66+
location,
67+
typeSpecName,
68+
typeof error,
69+
);
70+
}
71+
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
72+
// Only monitor this failure once because there tends to be a lot of the
73+
// same error.
74+
loggedTypeFailures[error.message] = true;
75+
consoleError('Failed %s type: %s', location, error.message);
76+
}
77+
}
78+
}
79+
}
80+
}

yarn.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3553,7 +3553,7 @@ prompts@^2.0.1:
35533553
kleur "^3.0.3"
35543554
sisteransi "^1.0.3"
35553555

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

0 commit comments

Comments
 (0)