diff --git a/lib/__tests__/fixtures/components/User.jsx b/lib/__tests__/fixtures/components/User.jsx
index beea08f..5dabffc 100755
--- a/lib/__tests__/fixtures/components/User.jsx
+++ b/lib/__tests__/fixtures/components/User.jsx
@@ -1,3 +1,4 @@
+import PropTypes from 'prop-types';
import React from 'react';
import { deps } from '../../../';
@@ -11,11 +12,11 @@ export default deps(({ userId }) => ({
static displayName = 'User';
static propTypes = {
- deleteUser: React.PropTypes.func,
- rank: React.PropTypes.string,
- updateUser: React.PropTypes.func,
- userId: React.PropTypes.number,
- userName: React.PropTypes.string,
+ deleteUser: PropTypes.func,
+ rank: PropTypes.string,
+ updateUser: PropTypes.func,
+ userId: PropTypes.number,
+ userName: PropTypes.string,
};
constructor(props) {
diff --git a/lib/__tests__/fixtures/components/Users.jsx b/lib/__tests__/fixtures/components/Users.jsx
index acf8a66..baceec0 100755
--- a/lib/__tests__/fixtures/components/Users.jsx
+++ b/lib/__tests__/fixtures/components/Users.jsx
@@ -1,3 +1,4 @@
+import PropTypes from 'prop-types';
import React from 'react';
import { deps, Store } from '../../../';
@@ -17,14 +18,14 @@ export default deps(() => ({
static displayName = 'Users';
static propTypes = {
- createUser: React.PropTypes.func,
+ createUser: PropTypes.func,
optionalStore: Store.State.propType(React.any),
- toggleUsersVisibility: React.PropTypes.func,
- uiUsersVisibility: Store.State.propType(React.PropTypes.bool.isRequired).isRequired,
- users: Store.State.propType(React.PropTypes.arrayOf(React.PropTypes.shape({
- userId: React.PropTypes.number.isRequired,
- userName: React.PropTypes.string.isRequired,
- rank: React.PropTypes.string.isRequired,
+ toggleUsersVisibility: PropTypes.func,
+ uiUsersVisibility: Store.State.propType(PropTypes.bool.isRequired).isRequired,
+ users: Store.State.propType(PropTypes.arrayOf(PropTypes.shape({
+ userId: PropTypes.number.isRequired,
+ userName: PropTypes.string.isRequired,
+ rank: PropTypes.string.isRequired,
}))).isRequired,
};
diff --git a/lib/__tests__/node/components.js b/lib/__tests__/node/components.js
index 681565d..30699f0 100644
--- a/lib/__tests__/node/components.js
+++ b/lib/__tests__/node/components.js
@@ -1,4 +1,5 @@
-import React, { Component, PropTypes as T } from 'react';
+import PropTypes from 'prop-types';
+import React, { Component } from 'react';
import stores from '../../stores';
import Store from '../../Store';
import MemoryStore from '../../MemoryStore';
@@ -18,7 +19,7 @@ describe('stores', () => {
class Bar extends Component {
static displayName = 'Bar';
static propTypes = {
- foo: Store.State.propType(T.string),
+ foo: Store.State.propType(PropTypes.string),
};
render() {
const { foo } = this.props;
diff --git a/lib/__tests__/node/prepare.js b/lib/__tests__/node/prepare.js
index bdc072f..5968acc 100644
--- a/lib/__tests__/node/prepare.js
+++ b/lib/__tests__/node/prepare.js
@@ -3,6 +3,7 @@ import Promise from 'bluebird';
import HTTPStatus from 'http-status-codes';
import _ from 'lodash';
import nock from 'nock';
+import PropTypes from 'prop-types';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import should from 'should/as-function';
@@ -91,7 +92,15 @@ describe('prepare', () => {
should(prepareInner).have.property('calledOnce').which.is.exactly(true);
});
});
-
+ it('prepares stateless components correctly', async function test() {
+ const StatelessComponent = () => null;
+ StatelessComponent[preparable.$prepare] = async function preps(props, context) {
+ context.bar = await Promise.resolve('foo');
+ };
+ const context = {};
+ return await prepare(, context)
+ .then(() => should(context).have.property('bar').which.is.exactly('foo'));
+ });
it('doesn’t fetch the same HTTPStore more than once', async function test() {
const testHttpConf = {
protocol: 'http',
@@ -112,7 +121,7 @@ describe('prepare', () => {
@stores(getStoreDeps)
class Child extends React.Component {
static propTypes = {
- testStore: React.PropTypes.object,
+ testStore: PropTypes.object,
};
render() {
return {this.props.testStore.value.message};
diff --git a/lib/actions.js b/lib/actions.js
index 71dbcb4..a7a9e83 100755
--- a/lib/actions.js
+++ b/lib/actions.js
@@ -1,10 +1,11 @@
+import PropTypes from 'prop-types';
import React from 'react';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
import _ from 'lodash';
const __DEV__ = process && process.env && process.env.NODE_ENV === 'development';
import Flux from './Flux';
import defaultFluxKey from './defaultFluxKey';
+import shouldPureComponentUpdate from './util/shouldPureComponentUpdate';
/**
* Enhance a React Component and make context's {@link Flux}'s {@link Actions}s requested by bindings avaliable as props.
@@ -18,7 +19,7 @@ import defaultFluxKey from './defaultFluxKey';
function actions(getBindings, {
displayName = void 0,
fluxKey = defaultFluxKey,
- shouldNexusComponentUpdate = PureRenderMixin.shouldComponentUpdate,
+ shouldNexusComponentUpdate = shouldPureComponentUpdate,
} = {}) {
return (Component) => {
/**
@@ -28,7 +29,7 @@ function actions(getBindings, {
static displayName = displayName || `@actions(${Component.displayName})`;
static contextTypes = {
- [fluxKey]: React.PropTypes.instanceOf(Flux),
+ [fluxKey]: PropTypes.instanceOf(Flux),
};
/**
diff --git a/lib/preparable.js b/lib/preparable.js
index 71c95a7..b3611f5 100755
--- a/lib/preparable.js
+++ b/lib/preparable.js
@@ -1,6 +1,4 @@
-import React from 'react';
-
-import isExtensionOf from './util/isExtensionOf';
+import isReactComponent from './util/isReactComponent';
const $prepare = Symbol('preparable');
/**
@@ -14,7 +12,7 @@ function preparable(prepare) {
throw new TypeError('@preparable() should be passed an async function');
}
return function extendComponent(Component) {
- if(!isExtensionOf(Component, React.Component)) {
+ if(!isReactComponent(Component)) {
throw new TypeError('@preparable should only be applied to React Components');
}
return class extends Component {
diff --git a/lib/prepare.js b/lib/prepare.js
index c6d26af..2d0b729 100755
--- a/lib/prepare.js
+++ b/lib/prepare.js
@@ -2,7 +2,7 @@ import Promise from 'bluebird';
import React from 'react';
import _ from 'lodash';
-import isExtensionOf from './util/isExtensionOf';
+import isReactComponent from './util/isReactComponent';
import preparable from './preparable';
const { $prepare } = preparable;
@@ -81,12 +81,12 @@ async function prepareElement(element, context) {
if(typeof type === 'string') {
return [props.children, context];
}
+ await satisfy(element, context);
// Function component (new in react 0.14.x)
- if(!isExtensionOf(type, React.Component)) {
+ if(!isReactComponent(type)) {
return [type(props), context];
}
// Composite element
- await satisfy(element, context);
let inst = null;
try {
inst = create(type, props, context);
diff --git a/lib/root.js b/lib/root.js
index ff181d9..188fe91 100644
--- a/lib/root.js
+++ b/lib/root.js
@@ -1,3 +1,4 @@
+import PropTypes from 'prop-types';
import React from 'react';
import Flux from './Flux';
@@ -20,11 +21,11 @@ function root({ fluxKey = defaultFluxKey, displayName = void 0 } = {}) {
static displayName = displayName || `@root(${Component.displayName})`;
static propTypes = {
- flux: React.PropTypes.instanceOf(Flux),
+ flux: PropTypes.instanceOf(Flux),
};
static childContextTypes = {
- [fluxKey]: React.PropTypes.instanceOf(Flux),
+ [fluxKey]: PropTypes.instanceOf(Flux),
};
getChildContext() {
diff --git a/lib/stores.js b/lib/stores.js
index c916d4a..82ba796 100755
--- a/lib/stores.js
+++ b/lib/stores.js
@@ -1,6 +1,6 @@
import Promise from 'bluebird';
+import PropTypes from 'prop-types';
import React from 'react';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
import _ from 'lodash';
import deepEqual from 'deep-equal';
const __DEV__ = process && process.env && process.NODE_ENV === 'development';
@@ -9,6 +9,7 @@ import defaultFluxKey from './defaultFluxKey';
import diff from './util/diff';
import Flux from './Flux';
import preparable from './preparable';
+import shouldPureComponentUpdate from './util/shouldPureComponentUpdate';
/**
* Enhance a React Component and make context's {@link Flux}'s {@link Store}'s {@link State}s requested by bindings
@@ -24,7 +25,7 @@ function stores(
getBindings, {
displayName = void 0,
fluxKey = defaultFluxKey,
- shouldNexusComponentUpdate = PureRenderMixin.shouldComponentUpdate,
+ shouldNexusComponentUpdate = shouldPureComponentUpdate,
} = {}) {
return (Component) => preparable(async function prepare(props, context) {
const flux = context[fluxKey];
@@ -46,7 +47,7 @@ function stores(
static displayName = displayName || `@stores(${Component.displayName})`;
static contextTypes = {
- [fluxKey]: React.PropTypes.instanceOf(Flux),
+ [fluxKey]: PropTypes.instanceOf(Flux),
};
/**
diff --git a/lib/util/__tests__/isExtensionOf.js b/lib/util/__tests__/isExtensionOf.js
deleted file mode 100644
index 632dc81..0000000
--- a/lib/util/__tests__/isExtensionOf.js
+++ /dev/null
@@ -1,22 +0,0 @@
-const { describe, it } = global;
-import isExtensionOf from '../isExtensionOf';
-import should from 'should/as-function';
-
-describe('isExtensionOf(B, A)', () => {
- it('returns true when B extends A', () => {
- class A {}
- class B extends A {}
- should(isExtensionOf(B, A)).be.true();
- });
- it('returns false when B doesn\'t extend A', () => {
- class A {}
- class B {}
- should(isExtensionOf(B, A)).not.be.true();
- });
- it('returns true when B extends C which extends A', () => {
- class A {}
- class C extends A {}
- class B extends C {}
- should(isExtensionOf(B, A)).be.true();
- });
-});
diff --git a/lib/util/__tests__/isReactComponent.js b/lib/util/__tests__/isReactComponent.js
new file mode 100644
index 0000000..35e65c4
--- /dev/null
+++ b/lib/util/__tests__/isReactComponent.js
@@ -0,0 +1,27 @@
+import React from 'react';
+import isReactComponent from '../isReactComponent';
+import should from 'should/as-function';
+const { describe, it } = global;
+
+describe('isReactComponent(type)', () => {
+ it('returns true on React.Component', () => {
+ class C extends React.Component {
+ render() {
+ return null;
+ }
+ }
+ should(isReactComponent(C)).be.true();
+ });
+ it('returns true on React.PureComponent', () => {
+ class C extends React.PureComponent {
+ render() {
+ return null;
+ }
+ }
+ should(isReactComponent(C)).be.true();
+ });
+ it('returns false on a functional component', () => {
+ const C = () => (
{'foo'}
);
+ should(isReactComponent(C)).be.false();
+ });
+});
diff --git a/lib/util/isExtensionOf.js b/lib/util/isExtensionOf.js
deleted file mode 100755
index aa79732..0000000
--- a/lib/util/isExtensionOf.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Determines whether a class A is an extension (direct or indirect) of another ancestor class B, by recursively
- * walking up the proto chain.
- * @param {Function} A Class that could be an extension
- * @param {Function} B Class that could be an ancestor
- * @return {Boolean} True if A is an extension of B, false otherwise.
- */
-function isExtensionOf(A, B) {
- if(A === B) {
- return true;
- }
- if(typeof A !== 'function') {
- return false;
- }
- const protoOfA = Reflect.getPrototypeOf(A);
- if(typeof B !== 'function') {
- return protoOfA === B;
- }
- return isExtensionOf(protoOfA, B);
-}
-
-export default isExtensionOf;
diff --git a/lib/util/isReactComponent.js b/lib/util/isReactComponent.js
new file mode 100755
index 0000000..996364f
--- /dev/null
+++ b/lib/util/isReactComponent.js
@@ -0,0 +1,6 @@
+export default function isReactCompositeComponent(component) {
+ if(typeof component.prototype === 'object' && component.prototype.isReactComponent) {
+ return true;
+ }
+ return false;
+}
diff --git a/lib/util/shouldPureComponentUpdate.js b/lib/util/shouldPureComponentUpdate.js
new file mode 100644
index 0000000..7fa48f9
--- /dev/null
+++ b/lib/util/shouldPureComponentUpdate.js
@@ -0,0 +1,7 @@
+import shallowEqual from 'shallowequal';
+
+function shouldPureComponentUpdate(nextProps, nextState) {
+ return (!shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState));
+}
+
+export default shouldPureComponentUpdate;
diff --git a/package.json b/package.json
index 00df83c..e090b5d 100644
--- a/package.json
+++ b/package.json
@@ -100,10 +100,10 @@
"koa-router": "^5.3.0",
"mocha": "^2.3.4",
"nock": "^8.0.0",
- "react": "^15.3.0",
- "react-addons-pure-render-mixin": "^15.3.0",
- "react-addons-test-utils": "^15.3.0",
- "react-dom": "^15.3.0",
+ "prop-types": "^15.6.0",
+ "react": "^15.6.2",
+ "react-dom": "^15.6.2",
+ "react-test-renderer": "^16.0.0",
"rimraf": "^2.5.0",
"run-sequence": "^1.1.5",
"selenium-standalone": "^4.9.0",
@@ -125,10 +125,10 @@
"lodash": "^4.0.0",
"path-to-regexp": "^1.2.1",
"setimmediate": "^1.0.4",
- "should": "^8.1.1"
+ "shallowequal": "^1.0.2"
},
"peerDependencies": {
- "react": "^15.3.0",
- "react-dom": "^15.3.0"
+ "react": "^15.6.2",
+ "react-dom": "^15.6.2"
}
}