Skip to content

Commit 5095593

Browse files
committed
Merge remote-tracking branch 'jhollingworth/store-by-props'
2 parents 20d4d85 + 8bb6766 commit 5095593

File tree

2 files changed

+69
-10
lines changed

2 files changed

+69
-10
lines changed

src/components/createConnect.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ export default function createConnect(React) {
3434
// Helps track hot reloading.
3535
const version = nextVersion++;
3636

37-
function computeStateProps(context) {
38-
const state = context.store.getState();
37+
function computeStateProps(store) {
38+
const state = store.getState();
3939
const stateProps = finalMapStateToProps(state);
4040
invariant(
4141
isPlainObject(stateProps),
@@ -45,8 +45,8 @@ export default function createConnect(React) {
4545
return stateProps;
4646
}
4747

48-
function computeDispatchProps(context) {
49-
const { dispatch } = context.store;
48+
function computeDispatchProps(store) {
49+
const { dispatch } = store;
5050
const dispatchProps = finalMapDispatchToProps(dispatch);
5151
invariant(
5252
isPlainObject(dispatchProps),
@@ -72,7 +72,11 @@ export default function createConnect(React) {
7272
static WrappedComponent = WrappedComponent;
7373

7474
static contextTypes = {
75-
store: storeShape.isRequired
75+
store: storeShape
76+
};
77+
78+
static propTypes = {
79+
store: storeShape
7680
};
7781

7882
shouldComponentUpdate(nextProps, nextState) {
@@ -82,13 +86,22 @@ export default function createConnect(React) {
8286
constructor(props, context) {
8387
super(props, context);
8488
this.version = version;
85-
this.stateProps = computeStateProps(context);
86-
this.dispatchProps = computeDispatchProps(context);
89+
this.store = props.store || context.store;
90+
91+
invariant(this.store,
92+
`Could not find "store" in either the context or ` +
93+
`props of "${this.constructor.displayName}". ` +
94+
`Either wrap the root component in a <Provider>, ` +
95+
`or explicitly pass "store" as a prop to "${this.constructor.displayName}".`
96+
);
97+
98+
this.stateProps = computeStateProps(this.store);
99+
this.dispatchProps = computeDispatchProps(this.store);
87100
this.state = this.computeNextState();
88101
}
89102

90103
recomputeStateProps() {
91-
const nextStateProps = computeStateProps(this.context);
104+
const nextStateProps = computeStateProps(this.store);
92105
if (shallowEqual(nextStateProps, this.stateProps)) {
93106
return false;
94107
}
@@ -98,7 +111,7 @@ export default function createConnect(React) {
98111
}
99112

100113
recomputeDispatchProps() {
101-
const nextDispatchProps = computeDispatchProps(this.context);
114+
const nextDispatchProps = computeDispatchProps(this.store);
102115
if (shallowEqual(nextDispatchProps, this.dispatchProps)) {
103116
return false;
104117
}
@@ -128,7 +141,7 @@ export default function createConnect(React) {
128141

129142
trySubscribe() {
130143
if (shouldSubscribe && !this.unsubscribe) {
131-
this.unsubscribe = this.context.store.subscribe(::this.handleChange);
144+
this.unsubscribe = this.store.subscribe(::this.handleChange);
132145
this.handleChange();
133146
}
134147
}

test/components/connect.spec.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,6 +713,52 @@ describe('React', () => {
713713
expect(decorated.WrappedComponent).toBe(Container);
714714
});
715715

716+
it('should use the store from the props instead of from the context if present', () => {
717+
class Container extends Component {
718+
render() {
719+
return <div />;
720+
}
721+
}
722+
723+
let actualState;
724+
725+
const expectedState = { foos: {} };
726+
const decorator = connect(state => {
727+
actualState = state;
728+
return {};
729+
});
730+
const Decorated = decorator(Container);
731+
const mockStore = {
732+
dispatch: () => {},
733+
subscribe: () => {},
734+
getState: () => expectedState
735+
};
736+
737+
TestUtils.renderIntoDocument(<Decorated store={mockStore} />);
738+
739+
expect(actualState).toEqual(expectedState);
740+
});
741+
742+
it('should throw an error if the store is not in the props or context', () => {
743+
class Container extends Component {
744+
render() {
745+
return <div />;
746+
}
747+
}
748+
749+
const decorator = connect(() => {});
750+
const Decorated = decorator(Container);
751+
const expectedError =
752+
`Invariant Violation: Could not find "store" in either the context ` +
753+
`or props of "Connect(Container)". Either wrap the root component in a ` +
754+
`<Provider>, or explicitly pass "store" as a prop to "Connect(Container)".`;
755+
756+
expect(() => TestUtils.renderIntoDocument(<Decorated />)).toThrow(e => {
757+
expect(e.message).toEqual(expectedError);
758+
return true;
759+
});
760+
});
761+
716762
it('should return the instance of the wrapped component for use in calling child methods', () => {
717763
const store = createStore(() => ({}));
718764

0 commit comments

Comments
 (0)