Skip to content

Commit 4a5756e

Browse files
committed
Merge remote-tracking branch 'gnoff/master' into new-api
2 parents 1e9809e + c54a8b8 commit 4a5756e

File tree

11 files changed

+513
-422
lines changed

11 files changed

+513
-422
lines changed

src/components/createAll.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import createProvider from './createProvider';
22
import createProvideDecorator from './createProvideDecorator';
33

4-
import createConnector from './createConnector';
54
import createConnectDecorator from './createConnectDecorator';
65

76
export default function createAll(React) {
87
// Wrapper components
98
const Provider = createProvider(React);
10-
const Connector = createConnector(React);
119

1210
// Higher-order components (decorators)
1311
const provide = createProvideDecorator(React, Provider);
14-
const connect = createConnectDecorator(React, Connector);
12+
const connect = createConnectDecorator(React);
1513

16-
return { Provider, Connector, provide, connect };
14+
return { Provider, provide, connect };
1715
}

src/components/createConnectDecorator.js

Lines changed: 113 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,125 @@
1+
import createStoreShape from '../utils/createStoreShape';
12
import getDisplayName from '../utils/getDisplayName';
23
import shallowEqualScalar from '../utils/shallowEqualScalar';
4+
import shallowEqual from '../utils/shallowEqual';
5+
import isPlainObject from '../utils/isPlainObject';
6+
import wrapActionCreators from '../utils/wrapActionCreators';
7+
import invariant from 'invariant';
38

4-
export default function createConnectDecorator(React, Connector) {
5-
const { Component } = React;
9+
const emptySelector = () => ({});
610

7-
return function connect(select) {
8-
return DecoratedComponent => class ConnectorDecorator extends Component {
9-
static displayName = `Connector(${getDisplayName(DecoratedComponent)})`;
11+
const emptyBinder = () => ({});
12+
13+
const identityMerge = (slice, actionsCreators, props) => ({ ...props, ...slice, ...actionsCreators});
14+
15+
16+
export default function createConnectDecorator(React) {
17+
const { Component, PropTypes } = React;
18+
const storeShape = createStoreShape(PropTypes);
19+
20+
return function connect(select, dispatchBinder = emptyBinder, mergeHandler = identityMerge) {
21+
22+
const subscribing = select ? true : false;
23+
const selectState = select || emptySelector;
24+
const bindDispatch = isPlainObject(dispatchBinder) ? wrapActionCreators(dispatchBinder) : dispatchBinder;
25+
const merge = mergeHandler;
26+
27+
return DecoratedComponent => class ConnectDecorator extends Component {
28+
static displayName = `ConnectDecorator(${getDisplayName(DecoratedComponent)})`;
1029
static DecoratedComponent = DecoratedComponent;
1130

12-
shouldComponentUpdate(nextProps) {
13-
return !shallowEqualScalar(this.props, nextProps);
31+
static contextTypes = {
32+
store: storeShape.isRequired
33+
};
34+
35+
shouldComponentUpdate(nextProps, nextState) {
36+
return (this.subscribed && !this.isSliceEqual(this.state.slice, nextState.slice)) ||
37+
!shallowEqualScalar(this.props, nextProps);
1438
}
1539

16-
render() {
17-
return (
18-
<Connector select={state => select(state, this.props)}>
19-
{stuff => <DecoratedComponent {...stuff} {...this.props} />}
20-
</Connector>
40+
isSliceEqual(slice, nextSlice) {
41+
const isRefEqual = slice === nextSlice;
42+
if (isRefEqual) {
43+
return true;
44+
} else if (typeof slice !== 'object' || typeof nextSlice !== 'object') {
45+
return isRefEqual;
46+
}
47+
return shallowEqual(slice, nextSlice);
48+
}
49+
50+
constructor(props, context) {
51+
super(props, context);
52+
this.state = {
53+
...this.selectState(props, context),
54+
...this.bindDispatch(context)
55+
};
56+
}
57+
58+
componentDidMount() {
59+
if (subscribing) {
60+
this.subscribed = true;
61+
this.unsubscribe = this.context.store.subscribe(::this.handleChange);
62+
}
63+
}
64+
65+
componentWillUnmount() {
66+
if (subscribing) {
67+
this.unsubscribe();
68+
}
69+
}
70+
71+
handleChange(props = this.props) {
72+
const nextState = this.selectState(props, this.context);
73+
if (!this.isSliceEqual(this.state.slice, nextState.slice)) {
74+
this.setState(nextState);
75+
}
76+
}
77+
78+
selectState(props = this.props, context = this.context) {
79+
const state = context.store.getState();
80+
const slice = selectState(state);
81+
82+
invariant(
83+
isPlainObject(slice),
84+
'The return value of `select` prop must be an object. Instead received %s.',
85+
slice
2186
);
87+
88+
return { slice };
89+
}
90+
91+
bindDispatch(context = this.context) {
92+
const { dispatch } = context.store;
93+
const actionCreators = bindDispatch(dispatch);
94+
95+
invariant(
96+
isPlainObject(actionCreators),
97+
'The return value of `bindDispatch` prop must be an object. Instead received %s.',
98+
actionCreators
99+
);
100+
101+
return { actionCreators };
102+
}
103+
104+
merge(props = this.props, state = this.state) {
105+
const { slice, actionCreators } = state;
106+
const merged = merge(slice, actionCreators, props);
107+
108+
invariant(
109+
isPlainObject(merged),
110+
'The return value of `merge` prop must be an object. Instead received %s.',
111+
merged
112+
);
113+
114+
return merged;
115+
}
116+
117+
getUnderlyingRef() {
118+
return this.underlyingRef;
119+
}
120+
121+
render() {
122+
return <DecoratedComponent ref={component => (this.underlyingRef = component)} {...this.merge()} />;
22123
}
23124
};
24125
};

src/components/createConnector.js

Lines changed: 0 additions & 88 deletions
This file was deleted.

src/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import React from 'react';
22
import createAll from './components/createAll';
33

4-
export const { Provider, Connector, provide, connect } = createAll(React);
4+
export const { Provider, Connector, provide, connect, connectDeprecated } = createAll(React);

src/native.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import React from 'react-native';
22
import createAll from './components/createAll';
33

4-
export const { Provider, Connector, provide, connect } = createAll(React);
4+
export const { Provider, Connector, provide, connect, connectDeprecated } = createAll(React);

src/utils/wrapActionCreators.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { bindActionCreators } from 'redux';
2+
3+
export default function wrapActionCreators(actionCreators) {
4+
return dispatch => bindActionCreators(actionCreators, dispatch);
5+
}

0 commit comments

Comments
 (0)