diff --git a/package.json b/package.json
index 7a1f67b..8092d7b 100644
--- a/package.json
+++ b/package.json
@@ -82,7 +82,7 @@
"immutability-helper": "^2.1.2",
"preact-render-to-string": "^3.6.0",
"preact-transition-group": "^1.1.0",
- "proptypes": "^0.14.3",
+ "prop-types": "^15.5.8",
"standalone-react-addons-pure-render-mixin": "^0.1.1"
}
}
diff --git a/rollup.config.js b/rollup.config.js
index 9221e3d..d14decd 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -21,7 +21,7 @@ export default {
useStrict: false,
globals: {
'preact': 'preact',
- 'proptypes': 'PropTypes'
+ 'prop-types': 'PropTypes'
},
plugins: [
format==='umd' && memory({
diff --git a/src/index.js b/src/index.js
index aa9c2da..ef298f9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,4 @@
-import PropTypes from 'proptypes';
+import PropTypes from 'prop-types';
import { render as preactRender, cloneElement as preactCloneElement, h, Component as PreactComponent, options } from 'preact';
const version = '15.1.0'; // trick libraries to think we are react
@@ -270,10 +270,35 @@ function statelessComponentHook(Ctor) {
return Wrapped;
}
+function validatePropTypes (vnode) {
+ if (DEV) {
+ let componentClass = typeof vnode.nodeName === "function"
+ ? vnode.nodeName
+ : vnode.type;
+
+ if (typeof componentClass !== "function") return;
+ let name = componentClass.displayName || componentClass.name;
+ let propTypes = componentClass.propTypes;
+ if (propTypes) {
+ let props = vnode.props;
+ if (
+ !(vnode.props && vnode.props.children) &&
+ vnode.children &&
+ vnode.children.length
+ ) {
+ props.children = vnode.children;
+ }
+ propsHook(props);
+ PropTypes.checkPropTypes(propTypes, props, 'prop', name);
+ }
+ }
+}
function createElement(...args) {
upgradeToVNodes(args, 2);
- return normalizeVNode(h(...args));
+ let node = h(...args);
+ validatePropTypes(node);
+ return normalizeVNode(node);
}
@@ -293,7 +318,6 @@ function normalizeVNode(vnode) {
}
applyEventNormalization(vnode);
-
return vnode;
}
@@ -315,10 +339,11 @@ function cloneElement(element, props, ...children) {
else if (props && props.children) {
cloneArgs.push(props.children);
}
- return normalizeVNode(preactCloneElement(...cloneArgs));
+ let newNode = preactCloneElement(...cloneArgs);
+ validatePropTypes(newNode);
+ return normalizeVNode(newNode);
}
-
function isValidElement(element) {
return element && ((element instanceof VNode) || element.$$typeof===REACT_ELEMENT_TYPE);
}
@@ -489,7 +514,7 @@ function multihook(hooks, skipDuplicates) {
function newComponentHook(props, context) {
- propsHook.call(this, props, context);
+ propsHook(props, context);
this.componentWillReceiveProps = multihook([propsHook, this.componentWillReceiveProps || 'componentWillReceiveProps']);
this.render = multihook([propsHook, beforeRender, this.render || 'render', afterRender]);
}
@@ -509,24 +534,8 @@ function propsHook(props, context) {
props.children[0] = props.children;
}
}
-
- // add proptype checking
- if (DEV) {
- let ctor = typeof this==='function' ? this : this.constructor,
- propTypes = this.propTypes || ctor.propTypes;
- if (propTypes) {
- for (let prop in propTypes) {
- if (propTypes.hasOwnProperty(prop) && typeof propTypes[prop]==='function') {
- const displayName = this.displayName || ctor.name;
- let err = propTypes[prop](props, prop, displayName, 'prop');
- if (err) console.error(new Error(err.message || err));
- }
- }
- }
- }
}
-
function beforeRender(props) {
currentComponent = this;
}
diff --git a/test/component.js b/test/component.js
index cb2a877..d290d68 100644
--- a/test/component.js
+++ b/test/component.js
@@ -173,13 +173,13 @@ describe('components', () => {
});
describe('propTypes', () => {
- function checkPropTypes(Foo) {
+ function checkPropTypes(Foo, name = 'Foo') {
sinon.stub(console, 'error');
-
React.render(, scratch);
- expect(console.error).to.have.been.calledWithMatch({
- message: 'Required prop `func` was not specified in `Foo`.'
- });
+ expect(console.error).to.have.been.calledWithMatch(
+ 'Warning: Failed prop type: The prop `func` is marked as required in `' + name + '`, but its value is `undefined`.'
+ );
+ expect(console.error).to.have.been.called;
console.error.reset();
@@ -187,9 +187,17 @@ describe('components', () => {
expect(console.error).not.to.have.been.called;
React.render({}} bool="one" />, scratch);
- expect(console.error).to.have.been.calledWithMatch({
- message: 'Invalid prop `bool` of type `string` supplied to `Foo`, expected `boolean`.'
- });
+ expect(console.error).to.have.been.calledWithMatch(
+ 'Warning: Failed prop type: Invalid prop `bool` of type `string` supplied to `' + name + '`, expected `boolean`.'
+ );
+
+ console.error.reset();
+
+ const clone = React.cloneElement();
+ React.render(clone, scratch);
+ expect(console.error).to.have.been.calledWithMatch(
+ 'Warning: Failed prop type: Invalid prop `func` of type `string` supplied to `' + name + '`, expected `function`.'
+ );
console.error.restore();
}
@@ -209,7 +217,7 @@ describe('components', () => {
});
it('should support propTypes for createClass components', () => {
- const Foo = React.createClass({
+ const Bar = React.createClass({
propTypes: {
func: React.PropTypes.func.isRequired,
bool: React.PropTypes.bool
@@ -217,24 +225,24 @@ describe('components', () => {
render: () =>
});
- checkPropTypes(Foo);
+ checkPropTypes(Bar, 'Bar');
});
it('should support propTypes for pure components', () => {
- function Foo() { return ; }
- Foo.propTypes = {
+ function Baz() { return ; }
+ Baz.propTypes = {
func: React.PropTypes.func.isRequired,
bool: React.PropTypes.bool
};
- checkPropTypes(Foo);
+ checkPropTypes(Baz, 'Baz');
- const Foo2 = () => ;
- Foo2.displayName = 'Foo';
- Foo2.propTypes = {
+ const Bip = () => ;
+ Bip.displayName = 'Bip';
+ Bip.propTypes = {
func: React.PropTypes.func.isRequired,
bool: React.PropTypes.bool
};
- checkPropTypes(Foo2);
+ checkPropTypes(Bip, 'Bip');
});
});