Skip to content

Commit f7dc41d

Browse files
committed
Rewrite to improve speed
1 parent aee30e6 commit f7dc41d

File tree

4 files changed

+223
-185
lines changed

4 files changed

+223
-185
lines changed

src/components/createConnect.js

+86-71
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import createStoreShape from '../utils/createStoreShape';
2-
import shallowEqualScalar from '../utils/shallowEqualScalar';
32
import shallowEqual from '../utils/shallowEqual';
43
import isPlainObject from '../utils/isPlainObject';
54
import wrapActionCreators from '../utils/wrapActionCreators';
@@ -17,19 +16,6 @@ function getDisplayName(Component) {
1716
return Component.displayName || Component.name || 'Component';
1817
}
1918

20-
function areStatePropsEqual(stateProps, nextStateProps) {
21-
const isRefEqual = stateProps === nextStateProps;
22-
if (
23-
isRefEqual ||
24-
typeof stateProps !== 'object' ||
25-
typeof nextStateProps !== 'object'
26-
) {
27-
return isRefEqual;
28-
}
29-
30-
return shallowEqual(stateProps, nextStateProps);
31-
}
32-
3319
// Helps track hot reloading.
3420
let nextVersion = 0;
3521

@@ -48,6 +34,38 @@ export default function createConnect(React) {
4834
// Helps track hot reloading.
4935
const version = nextVersion++;
5036

37+
function computeStateProps(context) {
38+
const state = context.store.getState();
39+
const stateProps = finalMapStateToProps(state);
40+
invariant(
41+
isPlainObject(stateProps),
42+
'`mapStateToProps` must return an object. Instead received %s.',
43+
stateProps
44+
);
45+
return stateProps;
46+
}
47+
48+
function computeDispatchProps(context) {
49+
const { dispatch } = context.store;
50+
const dispatchProps = finalMapDispatchToProps(dispatch);
51+
invariant(
52+
isPlainObject(dispatchProps),
53+
'`mapDispatchToProps` must return an object. Instead received %s.',
54+
dispatchProps
55+
);
56+
return dispatchProps;
57+
}
58+
59+
function computeNextState(stateProps, dispatchProps, parentProps) {
60+
const mergedProps = finalMergeProps(stateProps, dispatchProps, parentProps);
61+
invariant(
62+
isPlainObject(mergedProps),
63+
'`mergeProps` must return an object. Instead received %s.',
64+
mergedProps
65+
);
66+
return mergedProps;
67+
}
68+
5169
return DecoratedComponent => class Connect extends Component {
5270
static displayName = `Connect(${getDisplayName(DecoratedComponent)})`;
5371
static DecoratedComponent = DecoratedComponent;
@@ -57,20 +75,52 @@ export default function createConnect(React) {
5775
};
5876

5977
shouldComponentUpdate(nextProps, nextState) {
60-
return (
61-
this.isSubscribed() &&
62-
!areStatePropsEqual(this.state.stateProps, nextState.stateProps)
63-
) || !shallowEqualScalar(this.props, nextProps);
78+
return !shallowEqual(this.state, nextState);
6479
}
6580

6681
constructor(props, context) {
6782
super(props, context);
6883
this.version = version;
6984
this.setUnderlyingRef = ::this.setUnderlyingRef;
70-
this.state = {
71-
...this.mapState(props, context),
72-
...this.mapDispatch(context)
73-
};
85+
86+
this.stateProps = computeStateProps(context);
87+
this.dispatchProps = computeDispatchProps(context);
88+
this.state = this.computeNextState();
89+
}
90+
91+
recomputeStateProps() {
92+
const nextStateProps = computeStateProps(this.context);
93+
if (shallowEqual(nextStateProps, this.stateProps)) {
94+
return false;
95+
}
96+
97+
this.stateProps = nextStateProps;
98+
return true;
99+
}
100+
101+
recomputeDispatchProps() {
102+
const nextDispatchProps = computeDispatchProps(this.context);
103+
if (shallowEqual(nextDispatchProps, this.dispatchProps)) {
104+
return false;
105+
}
106+
107+
this.dispatchProps = nextDispatchProps;
108+
return true;
109+
}
110+
111+
computeNextState(props = this.props) {
112+
return computeNextState(
113+
this.stateProps,
114+
this.dispatchProps,
115+
props
116+
);
117+
}
118+
119+
recomputeState(props = this.props) {
120+
const nextState = this.computeNextState(props);
121+
if (!shallowEqual(nextState, this.state)) {
122+
this.setState(nextState);
123+
}
74124
}
75125

76126
isSubscribed() {
@@ -85,7 +135,7 @@ export default function createConnect(React) {
85135
}
86136

87137
tryUnsubscribe() {
88-
if (this.isSubscribed()) {
138+
if (this.unsubscribe) {
89139
this.unsubscribe();
90140
this.unsubscribe = null;
91141
}
@@ -106,61 +156,26 @@ export default function createConnect(React) {
106156

107157
// Update the state and bindings.
108158
this.trySubscribe();
109-
this.setState({
110-
...this.mapState(),
111-
...this.mapDispatch()
112-
});
159+
this.recomputeStateProps();
160+
this.recomputeDispatchProps();
161+
this.recomputeState();
113162
}
114163
}
115164

116-
componentWillUnmount() {
117-
this.tryUnsubscribe();
118-
}
119-
120-
handleChange(props = this.props) {
121-
const nextState = this.mapState(props, this.context);
122-
if (!areStatePropsEqual(this.state.stateProps, nextState.stateProps)) {
123-
this.setState(nextState);
165+
componentWillReceiveProps(nextProps) {
166+
if (!shallowEqual(nextProps, this.props)) {
167+
this.recomputeState(nextProps);
124168
}
125169
}
126170

127-
mapState(props = this.props, context = this.context) {
128-
const state = context.store.getState();
129-
const stateProps = finalMapStateToProps(state);
130-
131-
invariant(
132-
isPlainObject(stateProps),
133-
'`mapStateToProps` must return an object. Instead received %s.',
134-
stateProps
135-
);
136-
137-
return { stateProps };
138-
}
139-
140-
mapDispatch(context = this.context) {
141-
const { dispatch } = context.store;
142-
const dispatchProps = finalMapDispatchToProps(dispatch);
143-
144-
invariant(
145-
isPlainObject(dispatchProps),
146-
'`mapDispatchToProps` must return an object. Instead received %s.',
147-
dispatchProps
148-
);
149-
150-
return { dispatchProps };
171+
componentWillUnmount() {
172+
this.tryUnsubscribe();
151173
}
152174

153-
merge(props = this.props, state = this.state) {
154-
const { stateProps, dispatchProps } = state;
155-
const merged = finalMergeProps(stateProps, dispatchProps, props);
156-
157-
invariant(
158-
isPlainObject(merged),
159-
'`mergeProps` must return an object. Instead received %s.',
160-
merged
161-
);
162-
163-
return merged;
175+
handleChange() {
176+
if (this.recomputeStateProps()) {
177+
this.recomputeState();
178+
}
164179
}
165180

166181
getUnderlyingRef() {
@@ -174,7 +189,7 @@ export default function createConnect(React) {
174189
render() {
175190
return (
176191
<DecoratedComponent ref={this.setUnderlyingRef}
177-
{...this.merge()} />
192+
{...this.state} />
178193
);
179194
}
180195
};

src/utils/shallowEqualScalar.js

-34
This file was deleted.

0 commit comments

Comments
 (0)