From c33538de9fa11e8b86cf08707da258357a4170ea Mon Sep 17 00:00:00 2001 From: Tommy Leunen Date: Wed, 10 May 2017 11:27:05 -0700 Subject: [PATCH 1/3] Add a way to create a Provider using a specific store key --- src/components/Provider.js | 57 ++---------------------------- src/components/createProvider.js | 59 ++++++++++++++++++++++++++++++++ src/index.js | 3 +- test/components/Provider.spec.js | 38 +++++++++++++++----- 4 files changed, 93 insertions(+), 64 deletions(-) create mode 100644 src/components/createProvider.js diff --git a/src/components/Provider.js b/src/components/Provider.js index 10f2becd6..dce5cdd32 100644 --- a/src/components/Provider.js +++ b/src/components/Provider.js @@ -1,56 +1,3 @@ -import { Component, Children } from 'react' -import PropTypes from 'prop-types' -import { storeShape, subscriptionShape } from '../utils/PropTypes' -import warning from '../utils/warning' +import createProvider from './createProvider' -let didWarnAboutReceivingStore = false -function warnAboutReceivingStore() { - if (didWarnAboutReceivingStore) { - return - } - didWarnAboutReceivingStore = true - - warning( - ' does not support changing `store` on the fly. ' + - 'It is most likely that you see this error because you updated to ' + - 'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' + - 'automatically. See https://github.com/reactjs/react-redux/releases/' + - 'tag/v2.0.0 for the migration instructions.' - ) -} - -export default class Provider extends Component { - getChildContext() { - return { store: this.store, storeSubscription: null } - } - - constructor(props, context) { - super(props, context) - this.store = props.store - } - - render() { - return Children.only(this.props.children) - } -} - -if (process.env.NODE_ENV !== 'production') { - Provider.prototype.componentWillReceiveProps = function (nextProps) { - const { store } = this - const { store: nextStore } = nextProps - - if (store !== nextStore) { - warnAboutReceivingStore() - } - } -} - -Provider.propTypes = { - store: storeShape.isRequired, - children: PropTypes.element.isRequired -} -Provider.childContextTypes = { - store: storeShape.isRequired, - storeSubscription: subscriptionShape -} -Provider.displayName = 'Provider' +export default createProvider(); diff --git a/src/components/createProvider.js b/src/components/createProvider.js new file mode 100644 index 000000000..9463bd1fd --- /dev/null +++ b/src/components/createProvider.js @@ -0,0 +1,59 @@ +import { Component, Children } from 'react' +import PropTypes from 'prop-types' +import { storeShape, subscriptionShape } from '../utils/PropTypes' +import warning from '../utils/warning' + +let didWarnAboutReceivingStore = false +function warnAboutReceivingStore() { + if (didWarnAboutReceivingStore) { + return + } + didWarnAboutReceivingStore = true + + warning( + ' does not support changing `store` on the fly. ' + + 'It is most likely that you see this error because you updated to ' + + 'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' + + 'automatically. See https://github.com/reactjs/react-redux/releases/' + + 'tag/v2.0.0 for the migration instructions.' + ) +} + +export default (storeKey = 'store', subKey) => { + const subscriptionKey = subKey || `${storeKey}Subscription` + + class Provider extends Component { + getChildContext() { + return { [storeKey]: this[storeKey], [subscriptionKey]: null } + } + + constructor(props, context) { + super(props, context) + this[storeKey] = props.store; + } + + render() { + return Children.only(this.props.children) + } + } + + if (process.env.NODE_ENV !== 'production') { + Provider.prototype.componentWillReceiveProps = function (nextProps) { + if (this[storeKey] !== nextProps.store) { + warnAboutReceivingStore() + } + } + } + + Provider.propTypes = { + store: storeShape.isRequired, + children: PropTypes.element.isRequired, + }; + Provider.childContextTypes = { + [storeKey]: storeShape.isRequired, + [subscriptionKey]: subscriptionShape, + }; + Provider.displayName = 'Provider'; + + return Provider; +}; diff --git a/src/index.js b/src/index.js index 9ce59e7c4..996cc13cf 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import Provider from './components/Provider' +import createProvider from './components/createProvider' import connectAdvanced from './components/connectAdvanced' import connect from './connect/connect' -export { Provider, connectAdvanced, connect } +export { Provider, createProvider, connectAdvanced, connect } diff --git a/test/components/Provider.spec.js b/test/components/Provider.spec.js index d3bcfef66..ffc8ae498 100644 --- a/test/components/Provider.spec.js +++ b/test/components/Provider.spec.js @@ -5,20 +5,24 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import TestUtils from 'react-dom/test-utils' import { createStore } from 'redux' -import { Provider, connect } from '../../src/index' +import { Provider, createProvider, connect } from '../../src/index' describe('React', () => { describe('Provider', () => { - class Child extends Component { + const createChild = (storeKey = 'store') => { + class Child extends Component { + render() { + return
+ } + } - render() { - return
- } - } + Child.contextTypes = { + [storeKey]: PropTypes.object.isRequired + } - Child.contextTypes = { - store: PropTypes.object.isRequired + return Child } + const Child = createChild(); it('should enforce a single child', () => { const store = createStore(() => ({})) @@ -66,6 +70,24 @@ describe('React', () => { expect(child.context.store).toBe(store) }) + it('should add the store to the child context using a custom store key', () => { + const store = createStore(() => ({})) + const CustomProvider = createProvider('customStoreKey'); + const CustomChild = createChild('customStoreKey'); + + const spy = expect.spyOn(console, 'error'); + const tree = TestUtils.renderIntoDocument( + + + + ) + spy.destroy() + expect(spy.calls.length).toBe(0) + + const child = TestUtils.findRenderedComponentWithType(tree, CustomChild) + expect(child.context.customStoreKey).toBe(store) + }) + it('should warn once when receiving a new store in props', () => { const store1 = createStore((state = 10) => state + 1) const store2 = createStore((state = 10) => state * 2) From c19d6b02fcb1fcd06710873da8908f8914a52d32 Mon Sep 17 00:00:00 2001 From: Tommy Leunen Date: Wed, 10 May 2017 11:55:39 -0700 Subject: [PATCH 2/3] Move createProvider inside Provider.js --- src/components/Provider.js | 60 +++++++++++++++++++++++++++++++- src/components/createProvider.js | 59 ------------------------------- src/index.js | 3 +- 3 files changed, 60 insertions(+), 62 deletions(-) delete mode 100644 src/components/createProvider.js diff --git a/src/components/Provider.js b/src/components/Provider.js index dce5cdd32..acddc3272 100644 --- a/src/components/Provider.js +++ b/src/components/Provider.js @@ -1,3 +1,61 @@ -import createProvider from './createProvider' +import { Component, Children } from 'react' +import PropTypes from 'prop-types' +import { storeShape, subscriptionShape } from '../utils/PropTypes' +import warning from '../utils/warning' + +let didWarnAboutReceivingStore = false +function warnAboutReceivingStore() { + if (didWarnAboutReceivingStore) { + return + } + didWarnAboutReceivingStore = true + + warning( + ' does not support changing `store` on the fly. ' + + 'It is most likely that you see this error because you updated to ' + + 'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' + + 'automatically. See https://github.com/reactjs/react-redux/releases/' + + 'tag/v2.0.0 for the migration instructions.' + ) +} + +export function createProvider(storeKey = 'store', subKey) { + const subscriptionKey = subKey || `${storeKey}Subscription` + + class Provider extends Component { + getChildContext() { + return { [storeKey]: this[storeKey], [subscriptionKey]: null } + } + + constructor(props, context) { + super(props, context) + this[storeKey] = props.store; + } + + render() { + return Children.only(this.props.children) + } + } + + if (process.env.NODE_ENV !== 'production') { + Provider.prototype.componentWillReceiveProps = function (nextProps) { + if (this[storeKey] !== nextProps.store) { + warnAboutReceivingStore() + } + } + } + + Provider.propTypes = { + store: storeShape.isRequired, + children: PropTypes.element.isRequired, + }; + Provider.childContextTypes = { + [storeKey]: storeShape.isRequired, + [subscriptionKey]: subscriptionShape, + }; + Provider.displayName = 'Provider'; + + return Provider; +} export default createProvider(); diff --git a/src/components/createProvider.js b/src/components/createProvider.js deleted file mode 100644 index 9463bd1fd..000000000 --- a/src/components/createProvider.js +++ /dev/null @@ -1,59 +0,0 @@ -import { Component, Children } from 'react' -import PropTypes from 'prop-types' -import { storeShape, subscriptionShape } from '../utils/PropTypes' -import warning from '../utils/warning' - -let didWarnAboutReceivingStore = false -function warnAboutReceivingStore() { - if (didWarnAboutReceivingStore) { - return - } - didWarnAboutReceivingStore = true - - warning( - ' does not support changing `store` on the fly. ' + - 'It is most likely that you see this error because you updated to ' + - 'Redux 2.x and React Redux 2.x which no longer hot reload reducers ' + - 'automatically. See https://github.com/reactjs/react-redux/releases/' + - 'tag/v2.0.0 for the migration instructions.' - ) -} - -export default (storeKey = 'store', subKey) => { - const subscriptionKey = subKey || `${storeKey}Subscription` - - class Provider extends Component { - getChildContext() { - return { [storeKey]: this[storeKey], [subscriptionKey]: null } - } - - constructor(props, context) { - super(props, context) - this[storeKey] = props.store; - } - - render() { - return Children.only(this.props.children) - } - } - - if (process.env.NODE_ENV !== 'production') { - Provider.prototype.componentWillReceiveProps = function (nextProps) { - if (this[storeKey] !== nextProps.store) { - warnAboutReceivingStore() - } - } - } - - Provider.propTypes = { - store: storeShape.isRequired, - children: PropTypes.element.isRequired, - }; - Provider.childContextTypes = { - [storeKey]: storeShape.isRequired, - [subscriptionKey]: subscriptionShape, - }; - Provider.displayName = 'Provider'; - - return Provider; -}; diff --git a/src/index.js b/src/index.js index 996cc13cf..2e0c09220 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,4 @@ -import Provider from './components/Provider' -import createProvider from './components/createProvider' +import Provider, { createProvider } from './components/Provider' import connectAdvanced from './components/connectAdvanced' import connect from './connect/connect' From b27fd9b74c75823f020beb18c84c869497646429 Mon Sep 17 00:00:00 2001 From: Tommy Leunen Date: Wed, 10 May 2017 22:40:28 -0700 Subject: [PATCH 3/3] Remove semicolons --- src/components/Provider.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/Provider.js b/src/components/Provider.js index acddc3272..4ac44610b 100644 --- a/src/components/Provider.js +++ b/src/components/Provider.js @@ -48,14 +48,14 @@ export function createProvider(storeKey = 'store', subKey) { Provider.propTypes = { store: storeShape.isRequired, children: PropTypes.element.isRequired, - }; + } Provider.childContextTypes = { [storeKey]: storeShape.isRequired, [subscriptionKey]: subscriptionShape, - }; - Provider.displayName = 'Provider'; + } + Provider.displayName = 'Provider' - return Provider; + return Provider } -export default createProvider(); +export default createProvider()