Skip to content

Commit 576f3f2

Browse files
committed
Hacked-up version of Connect and Provider to use new context API
1 parent 5d792a2 commit 576f3f2

File tree

3 files changed

+137
-26
lines changed

3 files changed

+137
-26
lines changed

src/components/Provider.js

+37-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
import { Component, Children } from 'react'
1+
import React, { Component, Children } from 'react'
22
import PropTypes from 'prop-types'
33
import { storeShape, subscriptionShape } from '../utils/PropTypes'
44
import warning from '../utils/warning'
55

6+
import {ReactReduxContext} from "./context";
7+
68
let didWarnAboutReceivingStore = false
79
function warnAboutReceivingStore() {
810
if (didWarnAboutReceivingStore) {
@@ -20,20 +22,48 @@ function warnAboutReceivingStore() {
2022
}
2123

2224
export function createProvider(storeKey = 'store', subKey) {
23-
const subscriptionKey = subKey || `${storeKey}Subscription`
25+
//const subscriptionKey = subKey || `${storeKey}Subscription`
2426

2527
class Provider extends Component {
28+
/*
2629
getChildContext() {
2730
return { [storeKey]: this[storeKey], [subscriptionKey]: null }
2831
}
32+
*/
2933

3034
constructor(props, context) {
3135
super(props, context)
32-
this[storeKey] = props.store;
36+
//this[storeKey] = props.store;
37+
38+
const {store} = props;
39+
40+
if(!store || !store.getState || !store.dispatch) {
41+
throw new Error("Must pass a valid Redux store as a prop to Provider");
42+
}
43+
44+
this.state = {
45+
storeState : store.getState(),
46+
dispatch : store.dispatch,
47+
};
48+
}
49+
50+
componentDidMount() {
51+
const {store} = this.props;
52+
53+
this.unsubscribe = store.subscribe( () => {
54+
console.log("Provider subscription running");
55+
this.setState({storeState : store.getState()});
56+
});
3357
}
3458

3559
render() {
36-
return Children.only(this.props.children)
60+
console.log("Provider re-rendering");
61+
62+
return (
63+
<ReactReduxContext.Provider value={this.state}>
64+
{Children.only(this.props.children)}
65+
</ReactReduxContext.Provider>
66+
);
3767
}
3868
}
3969

@@ -45,14 +75,17 @@ export function createProvider(storeKey = 'store', subKey) {
4575
}
4676
}
4777

78+
4879
Provider.propTypes = {
4980
store: storeShape.isRequired,
5081
children: PropTypes.element.isRequired,
5182
}
83+
/*
5284
Provider.childContextTypes = {
5385
[storeKey]: storeShape.isRequired,
5486
[subscriptionKey]: subscriptionShape,
5587
}
88+
*/
5689

5790
return Provider
5891
}

src/components/connectAdvanced.js

+97-22
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import hoistStatics from 'hoist-non-react-statics'
22
import invariant from 'invariant'
3-
import { Component, createElement } from 'react'
3+
import React, { Component, createElement } from 'react'
44

55
import Subscription from '../utils/Subscription'
6+
import {ReactReduxContext} from "./context";
67
import { storeShape, subscriptionShape } from '../utils/PropTypes'
78

89
let hotReloadingVersion = 0
910
const dummyState = {}
1011
function noop() {}
11-
function makeSelectorStateful(sourceSelector, store) {
12+
function makeSelectorStateful(sourceSelector) {
1213
// wrap the selector in an object that tracks its results between runs.
1314
const selector = {
14-
run: function runComponentSelector(props) {
15+
run: function runComponentSelector(props, storeState) {
1516
try {
16-
const nextProps = sourceSelector(store.getState(), props)
17+
const nextProps = sourceSelector(storeState, props)
1718
if (nextProps !== selector.props || selector.error) {
1819
selector.shouldComponentUpdate = true
1920
selector.props = nextProps
@@ -78,17 +79,19 @@ export default function connectAdvanced(
7879
const subscriptionKey = storeKey + 'Subscription'
7980
const version = hotReloadingVersion++
8081

82+
/*
8183
const contextTypes = {
8284
[storeKey]: storeShape,
8385
[subscriptionKey]: subscriptionShape,
8486
}
8587
const childContextTypes = {
8688
[subscriptionKey]: subscriptionShape,
8789
}
90+
*/
8891

8992
return function wrapWithConnect(WrappedComponent) {
9093
invariant(
91-
typeof WrappedComponent == 'function',
94+
typeof WrappedComponent === 'function',
9295
`You must pass a component to the function returned by ` +
9396
`${methodName}. Instead received ${JSON.stringify(WrappedComponent)}`
9497
)
@@ -117,22 +120,30 @@ export default function connectAdvanced(
117120
super(props, context)
118121

119122
this.version = version
120-
this.state = {}
123+
//this.state = {}
121124
this.renderCount = 0
122-
this.store = props[storeKey] || context[storeKey]
123-
this.propsMode = Boolean(props[storeKey])
125+
//this.store = props[storeKey] || context[storeKey]
126+
//this.propsMode = Boolean(props[storeKey])
127+
128+
this.storeState = null;
129+
130+
124131
this.setWrappedInstance = this.setWrappedInstance.bind(this)
132+
this.renderChild = this.renderChild.bind(this);
125133

134+
/*
126135
invariant(this.store,
127136
`Could not find "${storeKey}" in either the context or props of ` +
128137
`"${displayName}". Either wrap the root component in a <Provider>, ` +
129138
`or explicitly pass "${storeKey}" as a prop to "${displayName}".`
130139
)
140+
*/
131141

132-
this.initSelector()
133-
this.initSubscription()
142+
//this.initSelector()
143+
//this.initSubscription()
134144
}
135145

146+
/*
136147
getChildContext() {
137148
// If this component received store from props, its subscription should be transparent
138149
// to any descendants receiving store+subscription from context; it passes along
@@ -141,6 +152,7 @@ export default function connectAdvanced(
141152
const subscription = this.propsMode ? null : this.subscription
142153
return { [subscriptionKey]: subscription || this.context[subscriptionKey] }
143154
}
155+
*/
144156

145157
componentDidMount() {
146158
if (!shouldHandleStateChanges) return
@@ -151,24 +163,28 @@ export default function connectAdvanced(
151163
// To handle the case where a child component may have triggered a state change by
152164
// dispatching an action in its componentWillMount, we have to re-run the select and maybe
153165
// re-render.
154-
this.subscription.trySubscribe()
155-
this.selector.run(this.props)
166+
//this.subscription.trySubscribe()
167+
this.selector.run(this.props, this.storeState);
156168
if (this.selector.shouldComponentUpdate) this.forceUpdate()
157169
}
158170

159-
componentWillReceiveProps(nextProps) {
160-
this.selector.run(nextProps)
171+
172+
UNSAFE_componentWillReceiveProps(nextProps) {
173+
this.selector.run(nextProps, this.storeState);
161174
}
162175

176+
177+
163178
shouldComponentUpdate() {
164179
return this.selector.shouldComponentUpdate
165180
}
166181

182+
167183
componentWillUnmount() {
168-
if (this.subscription) this.subscription.tryUnsubscribe()
169-
this.subscription = null
184+
//if (this.subscription) this.subscription.tryUnsubscribe()
185+
//this.subscription = null
170186
this.notifyNestedSubs = noop
171-
this.store = null
187+
//this.store = null
172188
this.selector.run = noop
173189
this.selector.shouldComponentUpdate = false
174190
}
@@ -185,13 +201,22 @@ export default function connectAdvanced(
185201
this.wrappedInstance = ref
186202
}
187203

204+
/*
188205
initSelector() {
189206
const sourceSelector = selectorFactory(this.store.dispatch, selectorFactoryOptions)
190207
this.selector = makeSelectorStateful(sourceSelector, this.store)
191208
this.selector.run(this.props)
192209
}
210+
*/
211+
212+
initSelector(dispatch, storeState) {
213+
const sourceSelector = selectorFactory(dispatch, selectorFactoryOptions)
214+
this.selector = makeSelectorStateful(sourceSelector)
215+
this.selector.run(this.props, storeState);
216+
}
193217

194218
initSubscription() {
219+
/*
195220
if (!shouldHandleStateChanges) return
196221
197222
// parentSub's source should match where store came from: props vs. context. A component
@@ -206,6 +231,7 @@ export default function connectAdvanced(
206231
// listeners logic is changed to not call listeners that have been unsubscribed in the
207232
// middle of the notification loop.
208233
this.notifyNestedSubs = this.subscription.notifyNestedSubs.bind(this.subscription)
234+
*/
209235
}
210236

211237
onStateChange() {
@@ -229,23 +255,69 @@ export default function connectAdvanced(
229255
this.notifyNestedSubs()
230256
}
231257

258+
/*
232259
isSubscribed() {
233260
return Boolean(this.subscription) && this.subscription.isSubscribed()
234261
}
262+
*/
235263

236264
addExtraProps(props) {
237-
if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
265+
//if (!withRef && !renderCountProp && !(this.propsMode && this.subscription)) return props
266+
if (!withRef && !renderCountProp) return props;
267+
268+
238269
// make a shallow copy so that fields added don't leak to the original selector.
239270
// this is especially important for 'ref' since that's a reference back to the component
240271
// instance. a singleton memoized selector would then be holding a reference to the
241272
// instance, preventing the instance from being garbage collected, and that would be bad
242273
const withExtras = { ...props }
243274
if (withRef) withExtras.ref = this.setWrappedInstance
244275
if (renderCountProp) withExtras[renderCountProp] = this.renderCount++
245-
if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
276+
//if (this.propsMode && this.subscription) withExtras[subscriptionKey] = this.subscription
246277
return withExtras
247278
}
248279

280+
renderChild(providerValue) {
281+
const {storeState, dispatch} = providerValue;
282+
283+
this.storeState = storeState;
284+
285+
//console.log(`Running renderChild (${displayName})`, storeState, this.props);
286+
287+
if(this.selector) {
288+
this.selector.run(this.props, storeState);
289+
}
290+
else {
291+
this.initSelector(dispatch, storeState);
292+
}
293+
294+
295+
296+
if (this.selector.error) {
297+
throw this.selector.error
298+
}
299+
else if(this.selector.shouldComponentUpdate) {
300+
console.log(`Re-rendering component (${displayName})`, this.selector.props);
301+
this.selector.shouldComponentUpdate = false;
302+
this.renderedElement = createElement(WrappedComponent, this.addExtraProps(this.selector.props));
303+
}
304+
else {
305+
//console.log(`Returning existing render result (${displayName})`, this.props)
306+
}
307+
308+
return this.renderedElement;
309+
}
310+
311+
render() {
312+
return (
313+
<ReactReduxContext.Consumer>
314+
{this.renderChild}
315+
</ReactReduxContext.Consumer>
316+
)
317+
}
318+
319+
/*
320+
249321
render() {
250322
const selector = this.selector
251323
selector.shouldComponentUpdate = false
@@ -256,14 +328,16 @@ export default function connectAdvanced(
256328
return createElement(WrappedComponent, this.addExtraProps(selector.props))
257329
}
258330
}
331+
*/
259332
}
260333

261334
Connect.WrappedComponent = WrappedComponent
262335
Connect.displayName = displayName
263-
Connect.childContextTypes = childContextTypes
264-
Connect.contextTypes = contextTypes
265-
Connect.propTypes = contextTypes
336+
//Connect.childContextTypes = childContextTypes
337+
//Connect.contextTypes = contextTypes
338+
//Connect.propTypes = contextTypes
266339

340+
/*
267341
if (process.env.NODE_ENV !== 'production') {
268342
Connect.prototype.componentWillUpdate = function componentWillUpdate() {
269343
// We are hot reloading!
@@ -290,6 +364,7 @@ export default function connectAdvanced(
290364
}
291365
}
292366
}
367+
*/
293368

294369
return hoistStatics(Connect, WrappedComponent)
295370
}

src/components/context.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import React from "react";
2+
3+
export const ReactReduxContext = React.createContext(null);

0 commit comments

Comments
 (0)