Skip to content

Commit 4855c84

Browse files
committed
small optimizations/refactors
1 parent 3c222f2 commit 4855c84

File tree

2 files changed

+104
-53
lines changed

2 files changed

+104
-53
lines changed

src/components/connectAdvanced.js

+62-53
Original file line numberDiff line numberDiff line change
@@ -87,24 +87,41 @@ export default function connectAdvanced(
8787

8888
const displayName = getDisplayName(wrappedComponentName)
8989

90-
class PureWrapper extends Component {
91-
shouldComponentUpdate(nextProps) {
92-
return nextProps.derivedProps !== this.props.derivedProps
93-
}
90+
let PureWrapper
9491

95-
render() {
96-
let { forwardRef, derivedProps } = this.props
97-
return <WrappedComponent {...derivedProps} ref={forwardRef} />
92+
if (withRef) {
93+
class PureWrapperRef extends Component {
94+
shouldComponentUpdate(nextProps) {
95+
return nextProps.derivedProps !== this.props.derivedProps
96+
}
97+
98+
render() {
99+
let { forwardRef, derivedProps } = this.props
100+
return <WrappedComponent {...derivedProps} ref={forwardRef} />
101+
}
98102
}
99-
}
103+
PureWrapperRef.propTypes = {
104+
derivedProps: propTypes.object,
105+
forwardRef: propTypes.oneOfType([
106+
propTypes.func,
107+
propTypes.object
108+
])
109+
}
110+
PureWrapper = PureWrapperRef
111+
} else {
112+
class PureWrapperNoRef extends Component {
113+
shouldComponentUpdate(nextProps) {
114+
return nextProps.derivedProps !== this.props.derivedProps
115+
}
100116

101-
PureWrapper.propTypes = {
102-
count: propTypes.object,
103-
derivedProps: propTypes.object,
104-
forwardRef: propTypes.oneOfType([
105-
propTypes.func,
106-
propTypes.object
107-
])
117+
render() {
118+
return <WrappedComponent {...this.props.derivedProps} />
119+
}
120+
}
121+
PureWrapperNoRef.propTypes = {
122+
derivedProps: propTypes.object,
123+
}
124+
PureWrapper = PureWrapperNoRef
108125
}
109126

110127
const selectorFactoryOptions = {
@@ -125,48 +142,31 @@ export default function connectAdvanced(
125142
class Connect extends OuterBase {
126143
constructor(props) {
127144
super(props)
128-
if (withRef) {
129-
invariant(!props.props[storeKey],
130-
'Passing redux store in props has been removed and does not do anything. ' +
131-
'To use a custom redux store for a single component, ' +
132-
'create a custom React context with React.createContext() and pass the Provider to react-redux\'s provider ' +
133-
'and the Consumer to this component as in <Provider context={context.Provider}><' +
134-
wrappedComponentName + ' consumer={context.Consumer} /></Provider>'
135-
)
136-
} else {
137-
invariant(!props[storeKey],
138-
'Passing redux store in props has been removed and does not do anything. ' +
139-
'To use a custom redux store for a single component, ' +
140-
'create a custom React context with React.createContext() and pass the Provider to react-redux\'s provider ' +
141-
'and the Consumer to this component as in <Provider context={context.Provider}><' +
142-
wrappedComponentName + ' consumer={context.Consumer} /></Provider>'
143-
)
144-
145-
}
146-
this.memoizeDerivedProps = this.makeMemoizer()
145+
invariant(withRef ? !props.props[storeKey] : !props[storeKey],
146+
'Passing redux store in props has been removed and does not do anything. ' +
147+
'To use a custom redux store for a single component, ' +
148+
'create a custom React context with React.createContext() and pass the Provider to react-redux\'s provider ' +
149+
'and the Consumer to this component as in <Provider context={context.Provider}><' +
150+
wrappedComponentName + ' consumer={context.Consumer} /></Provider>'
151+
)
152+
this.generatedDerivedProps = this.makeDerivedPropsGenerator()
147153
this.renderWrappedComponent = this.renderWrappedComponent.bind(this)
148154
}
149155

150-
makeMemoizer() {
156+
makeDerivedPropsGenerator() {
151157
let lastProps
152158
let lastState
153159
let lastDerivedProps
154160
let lastStore
155161
let sourceSelector
156-
let called = false
157162
return (state, props, store) => {
158-
if (called) {
159-
const sameProps = connectOptions.pure && lastProps === props
160-
const sameState = lastState === state
161-
if (sameProps && sameState) {
162-
return lastDerivedProps
163-
}
163+
if ((connectOptions.pure && lastProps === props) && (lastState === state)) {
164+
return lastDerivedProps
164165
}
165166
if (store !== lastStore) {
166167
lastStore = store
167168
sourceSelector = selectorFactory(store.dispatch, selectorFactoryOptions)
168169
}
169-
called = true
170170
lastProps = props
171171
lastState = state
172172
const nextProps = sourceSelector(state, props)
@@ -178,24 +178,32 @@ export default function connectAdvanced(
178178
}
179179
}
180180

181-
renderWrappedComponent(value) {
181+
renderWrappedComponentWithRef(value) {
182182
invariant(value,
183183
`Could not find "store" in the context of ` +
184184
`"${displayName}". Either wrap the root component in a <Provider>, ` +
185185
`or pass a custom React context provider to <Provider> and the corresponding ` +
186186
`React context consumer to ${displayName} in connect options.`
187187
)
188188
const { state, store } = value
189-
if (withRef) {
190-
const { forwardRef, props } = this.props
191-
let derivedProps = this.memoizeDerivedProps(state, props, store)
192-
if (connectOptions.pure) {
193-
return <PureWrapper derivedProps={derivedProps} forwardRef={forwardRef} />
194-
}
195-
196-
return <WrappedComponent {...derivedProps} ref={forwardRef} />
189+
const { forwardRef, props } = this.props
190+
let derivedProps = this.generatedDerivedProps(state, props, store)
191+
if (connectOptions.pure) {
192+
return <PureWrapper derivedProps={derivedProps} forwardRef={forwardRef} />
197193
}
198-
let derivedProps = this.memoizeDerivedProps(state, this.props, store)
194+
195+
return <WrappedComponent {...derivedProps} ref={forwardRef} />
196+
}
197+
198+
renderWrappedComponent(value) {
199+
invariant(value,
200+
`Could not find "store" in the context of ` +
201+
`"${displayName}". Either wrap the root component in a <Provider>, ` +
202+
`or pass a custom React context provider to <Provider> and the corresponding ` +
203+
`React context consumer to ${displayName} in connect options.`
204+
)
205+
const { state, store } = value
206+
let derivedProps = this.generatedDerivedProps(state, this.props, store)
199207
if (connectOptions.pure) {
200208
return <PureWrapper derivedProps={derivedProps} />
201209
}
@@ -215,6 +223,7 @@ export default function connectAdvanced(
215223
Connect.WrappedComponent = WrappedComponent
216224
Connect.displayName = displayName
217225
if (withRef) {
226+
Connect.prototype.renderWrappedComponent = Connect.prototype.renderWrappedComponentWithRef
218227
Connect.propTypes = {
219228
props: propTypes.object,
220229
forwardRef: propTypes.oneOfType([

test/components/connect.spec.js

+42
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,48 @@ describe('React', () => {
14931493
done()
14941494
})
14951495

1496+
it('should return the instance of the wrapped component for use in calling child methods, impure component', async (done) => {
1497+
const store = createStore(() => ({}))
1498+
1499+
const someData = {
1500+
some: 'data'
1501+
}
1502+
1503+
class Container extends Component {
1504+
someInstanceMethod() {
1505+
return someData
1506+
}
1507+
1508+
render() {
1509+
return <Passthrough loaded="yes" />
1510+
}
1511+
}
1512+
1513+
const decorator = connect(state => state, undefined, undefined, { withRef: 'forwardRef', pure: false })
1514+
const Decorated = decorator(Container)
1515+
1516+
const ref = React.createRef()
1517+
1518+
class Wrapper extends Component {
1519+
render() {
1520+
return (
1521+
<Decorated ref={ref}/>
1522+
)
1523+
}
1524+
}
1525+
1526+
const tester = rtl.render(
1527+
<ProviderMock store={store}>
1528+
<Wrapper />
1529+
</ProviderMock>
1530+
)
1531+
1532+
await rtl.waitForElement(() => tester.getByTestId('loaded'))
1533+
1534+
expect(ref.current.someInstanceMethod()).toBe(someData)
1535+
done()
1536+
})
1537+
14961538
it('should wrap impure components without supressing updates', () => {
14971539
const store = createStore(() => ({}))
14981540

0 commit comments

Comments
 (0)