Created
May 23, 2017 21:39
-
-
Save marcusradell/d8201412fa9addecef1e7e254c1d2f32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from "react"; | |
// @TODO: Import a local file that proxies rxjs so we can import a subset of the library. | |
import Rx from "rxjs"; | |
const epicsToUpdaters = (epics, actionsProxy) => { | |
const updaters = Object.keys(epics).reduce((accumulator, key) => { | |
const successKey = `${key}Success`; | |
const errorKey = `${key}Error`; | |
// eslint-disable-next-line no-param-reassign | |
accumulator[successKey] = epics[key].successUpdater; | |
// eslint-disable-next-line no-param-reassign | |
accumulator[errorKey] = epics[key].errorUpdater; | |
// eslint-disable-next-line no-param-reassign | |
accumulator[key] = eventData => state => { | |
epics[key] | |
.service() | |
.then( | |
result => | |
(result.errors | |
? actionsProxy.actions[errorKey](result.errors) | |
: actionsProxy.actions[successKey](result.data)) | |
); | |
return epics[key].actionUpdater(eventData)(state); | |
}; | |
return accumulator; | |
}, {}); | |
return updaters; | |
}; | |
export const connectModel = ({ initialState, updaters = {}, epics = {} }) => { | |
const actionsProxy = { actions: {} }; | |
const epicUpdaters = epicsToUpdaters(epics, actionsProxy); | |
const allUpdaters = Object.assign({}, updaters, epicUpdaters); | |
const actionsAndActionStreams = Object.keys(allUpdaters).reduce( | |
(accumulator, key) => { | |
const actionSubject = new Rx.Subject(); | |
// eslint-disable-next-line no-param-reassign | |
accumulator.actions[key] = data => { | |
actionSubject.next(data); | |
}; | |
// eslint-disable-next-line no-param-reassign | |
accumulator.actionStreams[key] = actionSubject.map(data => data); // removes .next | |
return accumulator; | |
}, | |
{ actions: {}, actionStreams: {} } | |
); | |
const { actions, actionStreams } = actionsAndActionStreams; | |
actionsProxy.actions = actions; | |
const updaterStreamsArray = Object.keys(actionStreams).map(key => | |
actionStreams[key].map(data => allUpdaters[key](data)) | |
); | |
const stateStream = Rx.Observable | |
.merge(...updaterStreamsArray) | |
.startWith(initialState) | |
.scan((state, updater) => updater(state)) | |
.shareReplay(1); | |
return { stateStream, actionStreams, actions }; | |
}; | |
export const connectView = ({ | |
connectedModel, | |
viewDataStream | |
}) => PureViewFactory => props => { | |
const { actions, stateStream: modelStateStream } = connectedModel; | |
const PureView = PureViewFactory({ props, actions }); | |
const viewStateStream = viewDataStream | |
? modelStateStream.combineLatest(viewDataStream, (self, viewState) => ({ | |
self, | |
viewState | |
})) | |
: modelStateStream; | |
class View extends Component { | |
componentDidMount() { | |
this.subscription = viewStateStream.subscribe( | |
state => { | |
console.log("this.setState", state); | |
this.setState(() => state); | |
}, | |
console.error, // eslint-disable-line no-console, | |
// @TODO: This should never happen, and should be removed when stable | |
() => { | |
console.log( | |
"Completed called inside componentDidMount in the connectView function" | |
); | |
} | |
); | |
} | |
componentWillUnmount() { | |
this.subscription.unsubscribe(); | |
} | |
render() { | |
return this.state && <PureView state={this.state} />; | |
} | |
} | |
return Object.assign({}, connectedModel, { | |
View, | |
PureView, | |
viewStateStream, | |
props | |
}); | |
}; | |
// @TODO: Separate view connector from model connector | |
export default props => model => PureViewFactory => | |
connectView({ connectedModel: connectModel(model) })(PureViewFactory)(props); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment